import { test, expect, type Page } from './fixtures';

// E2E for the admin Users page role toggle: flipping the Admin checkbox is a
// one-click privilege change, so it must confirm first, and the checkbox must
// never show a state the server didn't actually apply (cancel or failure keeps
// it on the true value).

const DESKTOP = { width: 1280, height: 720 } as const;

const adminUser = {
    id: 'u11111111-1111-1111-1111-111111111111',
    username: 'admin',
    created_at: '2026-01-01T00:00:00Z',
    is_admin: true
};

const bob = {
    id: 'b22222222-2222-2222-2222-222222222222',
    username: 'bob',
    created_at: '2026-02-01T00:00:00Z',
    is_admin: false
};

type Captured = { patched: boolean };

async function mockAdmin(page: Page, cap: Captured, opts: { patchStatus: number }) {
    await page.route('**/api/v1/auth/config', (r) =>
        r.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({ self_register_enabled: true, private_mode: false })
        })
    );
    await page.route('**/api/v1/auth/me', (r) =>
        r.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({ user: adminUser })
        })
    );
    await page.route('**/api/v1/auth/me/preferences', (r) =>
        r.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({ reader_mode: 'single', reader_page_gap: 'small' })
        })
    );
    await page.route('**/api/v1/me/bookmarks*', (r) =>
        r.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({ items: [], page: { limit: 50, offset: 0, total: 0 } })
        })
    );
    await page.route('**/api/v1/admin/system', (r) =>
        r.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({
                disk: null,
                memory: { total_bytes: 1, used_bytes: 0, percent_used: 0 },
                cpu: { percent_used: 0 },
                alerts: []
            })
        })
    );
    // The PATCH toggle — record whether it was ever sent.
    await page.route('**/api/v1/admin/users/*', async (r) => {
        cap.patched = true;
        await r.fulfill({
            status: opts.patchStatus,
            contentType: 'application/json',
            body:
                opts.patchStatus < 400
                    ? JSON.stringify({ ...bob, is_admin: true })
                    : JSON.stringify({ error: { code: 'internal_error', message: 'boom' } })
        });
    });
    // The user list (registered after the specific PATCH glob; list is GET).
    await page.route('**/api/v1/admin/users**', async (r) => {
        if (r.request().method() !== 'GET') return r.fallback();
        await r.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({
                items: [adminUser, bob],
                page: { limit: 100, offset: 0, total: 2 }
            })
        });
    });
}

test.describe('/admin/users role toggle', () => {
    test('cancelling the confirm leaves the checkbox unchanged and sends no request', async ({
        page
    }) => {
        const cap: Captured = { patched: false };
        await mockAdmin(page, cap, { patchStatus: 200 });
        page.on('dialog', (d) => d.dismiss());
        await page.setViewportSize(DESKTOP);
        await page.goto('/admin/users');

        const bobRow = page.locator('tr', { hasText: 'bob' });
        const checkbox = bobRow.getByLabel('admin');
        await expect(checkbox).not.toBeChecked();

        await checkbox.click();

        // Cancelled → no PATCH, and the box still reflects the server state.
        await expect(checkbox).not.toBeChecked();
        expect(cap.patched).toBe(false);
    });

    test('a failed toggle reverts the checkbox to the server state', async ({ page }) => {
        const cap: Captured = { patched: false };
        await mockAdmin(page, cap, { patchStatus: 500 });
        page.on('dialog', (d) => d.accept());
        await page.setViewportSize(DESKTOP);
        await page.goto('/admin/users');

        const bobRow = page.locator('tr', { hasText: 'bob' });
        const checkbox = bobRow.getByLabel('admin');
        await expect(checkbox).not.toBeChecked();

        await checkbox.click();

        // The PATCH was attempted and failed → the checkbox must snap back to
        // the actual (still non-admin) server state rather than stay flipped.
        await expect.poll(() => cap.patched).toBe(true);
        await expect(checkbox).not.toBeChecked();
    });
});
