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

test.describe('WFBO Deposit Management — Extended', () => {

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

    test('deposits page loads with table', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
    });

    test('shows Total Deposits and Total Withdrawals Today cards', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);

        const depositsCard = page.locator('[data-testid="stat-deposits-today"]');
        await expect(depositsCard).toBeVisible();
        await expect(depositsCard).toContainText('Total Deposits');
        await expect(depositsCard).toContainText('Today');
        await expect(depositsCard).toContainText('₱');
        const depositsPending = page.locator('[data-testid="stat-deposits-pending"]');
        await expect(depositsPending).toContainText('Pending');
        await expect(depositsPending).toContainText('₱');

        const withdrawalsCard = page.locator('[data-testid="stat-withdrawals-today"]');
        await expect(withdrawalsCard).toBeVisible();
        await expect(withdrawalsCard).toContainText('Total Withdrawals');
        await expect(withdrawalsCard).toContainText('Today');
        await expect(withdrawalsCard).toContainText('₱');
        const withdrawalsPending = page.locator('[data-testid="stat-withdrawals-pending"]');
        await expect(withdrawalsPending).toContainText('Pending');
        await expect(withdrawalsPending).toContainText('₱');
    });

    test('deposits table has Sent To column', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const header = page.locator('[data-testid="deposits-table"] th', { hasText: 'Sent To' });
        await expect(header).toBeVisible();
    });

    test('clicking a user in the deposits list opens their transactions page', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const userLink = page.locator('[data-testid^="link-deposit-user-"]').first();
        if (await userLink.count() === 0) {
            test.skip();
            return;
        }
        await userLink.click();
        await expect(page).toHaveURL(/\/wfbo\/users\/\d+\/transactions/);
    });

    test('status filter tabs are visible', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await expect(page.locator('[data-testid="deposit-status-filters"]')).toBeVisible();
    });

    test('clicking Approved filter reloads with filtered view', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const approvedFilter = page.locator('[data-testid="deposit-status-filters"]').locator('text=Approved');
        await approvedFilter.click();
        await page.waitForLoadState('networkidle');
        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
        expect(page.url()).toContain('status=approved');
    });

    test('clicking Rejected filter reloads with filtered view', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const rejectedFilter = page.locator('[data-testid="deposit-status-filters"]').locator('text=Rejected');
        await rejectedFilter.click();
        await page.waitForLoadState('networkidle');
        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
        expect(page.url()).toContain('status=rejected');
    });

    test('can approve a pending deposit if one exists', async ({ page }) => {
        // Seeded pending deposits have no payment method / assigned handler, so
        // only an admin (not a plain FINANCE user) can act on them.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const approveBtn = page.locator('[data-testid^="btn-approve-"]').first();
        if (await approveBtn.count() === 0) {
            test.skip();
            return;
        }
        const id = (await approveBtn.getAttribute('data-testid'))?.replace('btn-approve-', '');
        page.once('dialog', dialog => dialog.accept());
        await approveBtn.click();
        await page.waitForLoadState('networkidle');
        if (await page.locator(`[data-testid="deposit-row-${id}"]`).count() > 0) {
            await page.goto(`${BASE_URL}/wfbo/deposits`);
        }
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).not.toBeAttached({ timeout: 10000 });
    });

    test('can reject a pending deposit with a review note', async ({ page }) => {
        // Seeded pending deposits have no payment method / assigned handler, so
        // only an admin (not a plain FINANCE user) can act on them.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const rejectBtn = page.locator('[data-testid^="btn-reject-"]').first();
        if (await rejectBtn.count() === 0) {
            test.skip();
            return;
        }
        const id = (await rejectBtn.getAttribute('data-testid'))?.replace('btn-reject-', '');
        const noteInput = page.locator(`[data-testid="input-review-note-${id}"]`);
        if (await noteInput.count() > 0) {
            await noteInput.fill('Insufficient proof of payment');
        }
        page.once('dialog', dialog => dialog.accept());
        await rejectBtn.click();
        await page.waitForLoadState('networkidle');
        if (await page.locator(`[data-testid="deposit-row-${id}"]`).count() > 0) {
            await page.goto(`${BASE_URL}/wfbo/deposits`);
        }
        await expect(page.locator(`[data-testid="btn-reject-${id}"]`)).not.toBeAttached({ timeout: 10000 });
    });

    test('declining the confirmation leaves the deposit pending', async ({ page }) => {
        // Seeded pending deposits have no payment method / assigned handler, so
        // only an admin (not a plain FINANCE user) can act on them.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const approveBtn = page.locator('[data-testid^="btn-approve-"]').first();
        if (await approveBtn.count() === 0) {
            test.skip();
            return;
        }
        const id = (await approveBtn.getAttribute('data-testid'))?.replace('btn-approve-', '');

        let requestFired = false;
        await page.route(`**/wfbo/deposits/${id}/action/approve`, (route) => {
            requestFired = true;
            return route.continue();
        });

        page.once('dialog', dialog => dialog.dismiss());
        await approveBtn.click();
        await page.waitForTimeout(300);

        expect(requestFired).toBe(false);
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).toBeAttached();
        await expect(page.locator(`[data-testid="deposit-row-${id}"]`)).not.toHaveClass(/is-processing/);
    });

    test('shows a processing state on the row while the approval request is in flight', async ({ page }) => {
        // Seeded pending deposits have no payment method / assigned handler, so
        // only an admin (not a plain FINANCE user) can act on them.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const approveBtn = page.locator('[data-testid^="btn-approve-"]').first();
        if (await approveBtn.count() === 0) {
            test.skip();
            return;
        }
        const id = (await approveBtn.getAttribute('data-testid'))?.replace('btn-approve-', '');

        await page.route(`**/wfbo/deposits/${id}/action/approve`, async (route) => {
            await new Promise((resolve) => setTimeout(resolve, 800));
            await route.continue();
        });

        page.once('dialog', dialog => dialog.accept());
        await approveBtn.click();

        const row = page.locator(`[data-testid="deposit-row-${id}"]`);
        await expect(row).toHaveClass(/is-processing/);
        await expect(approveBtn).toContainText('Approving');
        await expect(approveBtn).toBeDisabled();
        await expect(page.locator(`[data-testid="btn-reject-${id}"]`)).toBeDisabled();

        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).not.toBeAttached({ timeout: 10000 });
    });

    test('FINANCE user can access deposits page', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
    });

    test('ADMIN user can access deposits page', async ({ page }) => {
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
    });

    test('OPERATIONS user is blocked from deposits page', async ({ page }) => {
        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await expect(page.locator('[data-testid="deposits-table"]')).not.toBeVisible();
    });

    test('search form is visible on deposits page', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await expect(page.locator('[data-testid="deposit-search-form"]')).toBeVisible();
        await expect(page.locator('[data-testid="input-deposit-search"]')).toBeVisible();
        await expect(page.locator('[data-testid="btn-deposit-search"]')).toBeVisible();
    });

    test('search input auto-submits via debounce without clicking Search', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await page.waitForLoadState('networkidle');
        // Focus and type to ensure reliable input events
        await page.locator('[data-testid="input-deposit-search"]').focus();
        await page.keyboard.type('DebounceAutoTest', { delay: 50 });
        // Debounce fires at 400ms; wait for navigation/URL change
        await page.waitForURL(/search=DebounceAutoTest/, { timeout: 10000 });
        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
    });

    test('sidebar deposit badge element exists in DOM for FINANCE user', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        // Badge span is rendered server-side and managed by wfbo-pending controller
        await expect(page.locator('[data-wfbo-pending-target="depositBadge"]')).toBeAttached();
    });

    test('searching by name filters the deposits table', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await page.fill('[data-testid="input-deposit-search"]', 'Test');
        await page.click('[data-testid="btn-deposit-search"]');
        await page.waitForLoadState('networkidle');

        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
        expect(page.url()).toContain('search=Test');
    });

    test('searching by mobile number filters the deposits table', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        await page.fill('[data-testid="input-deposit-search"]', '+1000');
        await page.click('[data-testid="btn-deposit-search"]');
        await page.waitForLoadState('networkidle');

        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
        expect(page.url()).toContain('search=');
    });

    test('clear button removes search and reloads', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits?search=Test`);
        await expect(page.locator('[data-testid="btn-deposit-search-clear"]')).toBeVisible();
        await page.click('[data-testid="btn-deposit-search-clear"]');
        await page.waitForLoadState('networkidle');

        expect(page.url()).not.toContain('search=');
        await expect(page.locator('[data-testid="deposits-table"]')).toBeVisible();
    });

    test('search preserves status filter', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits?status=pending`);
        await page.fill('[data-testid="input-deposit-search"]', 'User');
        await page.click('[data-testid="btn-deposit-search"]');
        await page.waitForLoadState('networkidle');

        expect(page.url()).toContain('status=pending');
        expect(page.url()).toContain('search=User');
    });

    test('view transaction button opens modal when deposit has an image', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const viewBtn = page.locator('[data-testid^="btn-view-transaction-image-"]').first();
        if (await viewBtn.count() === 0) {
            test.skip();
            return;
        }
        await viewBtn.click();
        await expect(page.locator('#deposit-image-modal')).toBeVisible();
    });

    // ── Overdue pending alert (fixtures seed 4 pending deposits ≥ 5 min old + 1 fresh) ──

    const overdueCountFromAlert = async (text: string): Promise<number> => {
        const m = text.match(/(\d+)\s+deposit request/);
        expect(m, `alert text "${text}" has a count`).not.toBeNull();
        return Number(m![1]);
    };

    test('overdue alert shows the count of pending deposits waiting 5+ minutes', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const alert = page.locator('[data-testid="deposits-overdue-alert"]');
        await expect(alert).toBeVisible();
        await expect(alert).toContainText('pending 5+ minutes');
        await expect(page.locator('[data-testid="btn-view-overdue"]')).toBeVisible();

        const n = await overdueCountFromAlert(await page.locator('[data-testid="overdue-alert-text"]').innerText());
        expect(n).toBeGreaterThanOrEqual(1);
    });

    test('View shows only the overdue pending requests, oldest first', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits`);
        const expected = await overdueCountFromAlert(await page.locator('[data-testid="overdue-alert-text"]').innerText());

        await page.click('[data-testid="btn-view-overdue"]');
        await page.waitForLoadState('networkidle');

        expect(page.url()).toContain('overdue=1');
        expect(page.url()).toContain('status=pending');
        await expect(page.locator('[data-testid="deposits-overdue-alert"]')).toContainText('oldest first');
        await expect(page.locator('[data-testid="btn-clear-overdue"]')).toBeVisible();

        const rows = page.locator('[data-testid^="deposit-row-"]');
        await expect(rows).toHaveCount(Math.min(expected, 15));
        for (const badge of await page.locator('[data-testid^="badge-deposit-"]').all()) {
            await expect(badge).toContainText('Pending');
        }

        // Overdue view is ordered oldest-first: the "Submitted" cell (6th column)
        // of row 1 should be no later than row 2's.
        if (await rows.count() >= 2) {
            const submitted = async (i: number) =>
                Date.parse(await rows.nth(i).locator('td').nth(5).innerText());
            const [a, b] = [await submitted(0), await submitted(1)];
            if (!Number.isNaN(a) && !Number.isNaN(b)) {
                expect(a).toBeLessThanOrEqual(b);
            }
        }
    });

    test('Show all exits overdue mode', async ({ page }) => {
        await page.goto(`${BASE_URL}/wfbo/deposits?status=pending&overdue=1`);
        await expect(page.locator('[data-testid="btn-clear-overdue"]')).toBeVisible();
        await page.click('[data-testid="btn-clear-overdue"]');
        await page.waitForLoadState('networkidle');

        expect(page.url()).not.toContain('overdue');
        await expect(page.locator('[data-testid="btn-view-overdue"]')).toBeVisible();
    });

});

test.describe('WFBO Deposit Management — handler restriction', () => {

    test('only the assigned finance handler can approve/reject a deposit for its MOP', async ({ page }) => {
        // The deposit form's dropdown renders "bankName - accountName (accountNumber)",
        // not the PaymentDetail's own `label` field - the bank name itself must be
        // unique to reliably find this option among others.
        const bankName = `Handler Test Bank ${Date.now()}`;
        const ref      = `TXN-HANDLER-${Date.now()}`;

        // Create a payment method assigned to FINANCE.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await page.goto(`${BASE_URL}/wfbo/payment-details/new`);
        await page.fill('[data-testid="input-label"]', bankName);
        await page.fill('[data-testid="input-bank-name"]', bankName);
        await page.fill('[data-testid="input-account-name"]', 'Test Account');
        await page.fill('[data-testid="input-account-number"]', `${Date.now()}`.slice(-10));
        await page.selectOption('[data-testid="select-finance-handler"]', { label: 'Finance User (+10000000004)' });
        await page.click('[data-testid="btn-submit"]');
        await expect(page).toHaveURL(`${BASE_URL}/wfbo/payment-details`);

        // Submit a deposit against that bank as a player.
        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/deposit`);
        const bankOption = page.locator('[data-testid="select-payment-detail"] option', { hasText: bankName });
        if (await bankOption.count() === 0) {
            test.skip();
            return;
        }
        const bankValue = await bankOption.getAttribute('value');
        await page.selectOption('[data-testid="select-payment-detail"]', bankValue!);
        await page.fill('[data-testid="input-amount"]', '150.00');
        await page.fill('[data-testid="input-transaction-reference"]', ref);
        await page.click('[data-testid="btn-deposit-submit"]');
        await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

        // Not the assigned handler: buttons hidden, "Assigned to" note shown.
        await loginAs(page, FINANCE2.mobile, FINANCE2.password);
        await page.goto(`${BASE_URL}/wfbo/deposits?status=pending`);
        const row = page.locator('[data-testid^="deposit-row-"]').filter({ hasText: ref });
        await expect(row).toBeVisible();
        const id = (await row.getAttribute('data-testid'))!.replace('deposit-row-', '');
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).not.toBeAttached();
        await expect(page.locator(`[data-testid="btn-reject-${id}"]`)).not.toBeAttached();
        await expect(page.locator(`[data-testid="deposit-review-restricted-${id}"]`)).toContainText('Assigned to Finance User');

        // Not even an admin can bypass the assigned handler.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await page.goto(`${BASE_URL}/wfbo/deposits?status=pending`);
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).not.toBeAttached();
        await expect(page.locator(`[data-testid="deposit-review-restricted-${id}"]`)).toBeVisible();

        // The assigned handler can approve it.
        await loginAs(page, FINANCE.mobile, FINANCE.password);
        await page.goto(`${BASE_URL}/wfbo/deposits?status=pending`);
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).toBeVisible();
        page.once('dialog', dialog => dialog.accept());
        await page.click(`[data-testid="btn-approve-${id}"]`);
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).not.toBeAttached({ timeout: 10000 });
    });

    test("assigned handler can transfer a deposit to another payment method's handler", async ({ page }) => {
        const bank1Name = `Transfer Test Bank A ${Date.now()}`;
        const bank2Name = `Transfer Test Bank B ${Date.now()}`;
        const ref       = `TXN-TRANSFER-${Date.now()}`;

        const createPaymentDetail = async (bankName: string, handlerLabel: string) => {
            await page.goto(`${BASE_URL}/wfbo/payment-details/new`);
            await page.fill('[data-testid="input-label"]', bankName);
            await page.fill('[data-testid="input-bank-name"]', bankName);
            await page.fill('[data-testid="input-account-name"]', 'Test Account');
            await page.fill('[data-testid="input-account-number"]', `${Date.now()}`.slice(-10));
            await page.selectOption('[data-testid="select-finance-handler"]', { label: handlerLabel });
            await page.click('[data-testid="btn-submit"]');
            await expect(page).toHaveURL(`${BASE_URL}/wfbo/payment-details`);
        };

        // Two payment methods, each assigned to a different finance user.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await createPaymentDetail(bank1Name, 'Finance User (+10000000004)');
        await createPaymentDetail(bank2Name, 'Finance2 User (+10000000010)');

        // Submit a deposit against bank 1 (assigned to FINANCE) as a player.
        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/deposit`);
        const bankOption = page.locator('[data-testid="select-payment-detail"] option', { hasText: bank1Name });
        if (await bankOption.count() === 0) {
            test.skip();
            return;
        }
        const bankValue = await bankOption.getAttribute('value');
        await page.selectOption('[data-testid="select-payment-detail"]', bankValue!);
        await page.fill('[data-testid="input-amount"]', '150.00');
        await page.fill('[data-testid="input-transaction-reference"]', ref);
        await page.click('[data-testid="btn-deposit-submit"]');
        await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

        // FINANCE (assigned to bank 1) transfers it to bank 2 (assigned to FINANCE2).
        await loginAs(page, FINANCE.mobile, FINANCE.password);
        await page.goto(`${BASE_URL}/wfbo/deposits?status=pending`);
        const row = page.locator('[data-testid^="deposit-row-"]').filter({ hasText: ref });
        await expect(row).toBeVisible();
        const id = (await row.getAttribute('data-testid'))!.replace('deposit-row-', '');

        const transferSelect = page.locator(`[data-testid="select-transfer-payment-detail-${id}"]`);
        const transferOption = transferSelect.locator('option', { hasText: bank2Name });
        const transferValue  = await transferOption.getAttribute('value');
        await transferSelect.selectOption(transferValue!);
        page.once('dialog', dialog => dialog.accept());
        await page.click(`[data-testid="btn-transfer-${id}"]`);
        await page.waitForLoadState('networkidle');
        await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

        // FINANCE no longer controls it - now shows "Assigned to Finance2 User".
        await page.goto(`${BASE_URL}/wfbo/deposits?status=pending`);
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).not.toBeAttached();
        await expect(page.locator(`[data-testid="deposit-review-restricted-${id}"]`)).toContainText('Assigned to Finance2 User');

        // FINANCE2 now has full control and can approve it.
        await loginAs(page, FINANCE2.mobile, FINANCE2.password);
        await page.goto(`${BASE_URL}/wfbo/deposits?status=pending`);
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).toBeVisible();
        page.once('dialog', dialog => dialog.accept());
        await page.click(`[data-testid="btn-approve-${id}"]`);
        await expect(page.locator(`[data-testid="btn-approve-${id}"]`)).not.toBeAttached({ timeout: 10000 });
    });

});
