import { test, expect } from '@playwright/test';
import { testing_event_id, mock_site_domain } from './_helpers/env';
import {
    minimal_event_for_leads,
    seed_ae_loc,
} from './_helpers/leads_helpers';

const event_id = testing_event_id;
const config_url = `/events/${event_id}/leads/config`;

/**
 * Minimal route mock for the Leads Config page.
 *
 * The config page only needs the event record (not exhibit or badge data) plus
 * the site_domain init call. A PATCH handler is included for save tests.
 *
 * @param patch_handler  Optional callback invoked when a PATCH to the event is intercepted.
 *                       Defaults to returning 200 OK.
 */
async function setup_config_routes(
    page: import('@playwright/test').Page,
    opts: {
        event_data_overrides?: Record<string, any>;
        on_patch?: (body: any) => void;
    } = {}
) {
    const { event_data_overrides = {}, on_patch } = opts;

    await page.route('**/v3/**', async (route) => {
        const req = route.request();
        const url = req.url();
        const method = req.method();

        // Site domain init
        if (url.includes('site_domain/search')) {
            return route.fulfill({
                status: 200,
                contentType: 'application/json',
                body: JSON.stringify({ data: [mock_site_domain] }),
            });
        }

        // Event GET — provides mod_exhibits_json for the draft
        if (url.includes(`/v3/crud/event/${event_id}`) && method === 'GET') {
            return route.fulfill({
                status: 200,
                contentType: 'application/json',
                body: JSON.stringify(minimal_event_for_leads(event_id, event_data_overrides)),
            });
        }

        // Event PATCH — save config
        if (url.includes(`/v3/crud/event/${event_id}`) && method === 'PATCH') {
            const raw = await req.postData();
            const body = raw ? JSON.parse(raw) : {};
            on_patch?.(body);
            return route.fulfill({
                status: 200,
                contentType: 'application/json',
                body: JSON.stringify({ data: { id: event_id } }),
            });
        }

        return route.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({ data: [] }),
        });
    });
}

test.describe('Leads — Config Page', () => {
    // -----------------------------------------------------------------------
    // 1. Non-admin user sees access-denied screen
    //
    // The config page is administrator_access only. Any other access level must
    // see the Lock icon and "Administrator access required." — no form fields.
    // -----------------------------------------------------------------------
    test('non-admin user sees access-denied message, no form', async ({ page }) => {
        page.on('pageerror', (err) => console.error(`BROWSER ERROR: ${err.message}`));
        await setup_config_routes(page);
        await seed_ae_loc(page, { allow_access: true, trusted_access: true });

        await page.goto(config_url);

        // Access denied block must be visible
        await expect(
            page.locator('text=Administrator access required.')
        ).toBeVisible({ timeout: 10_000 });

        // Form / save button must NOT be visible
        await expect(page.locator('text=Leads Config')).not.toBeVisible();
        await expect(page.locator('button:has-text("Save")')).not.toBeVisible();
    });

    // -----------------------------------------------------------------------
    // 2. Admin user sees config form
    //
    // administrator_access = true reveals the full form with both sections
    // (Payment and Stripe Keys) and the Save button.
    // -----------------------------------------------------------------------
    test('admin user sees config form with Payment and Stripe sections', async ({ page }) => {
        page.on('pageerror', (err) => console.error(`BROWSER ERROR: ${err.message}`));
        await setup_config_routes(page);
        await seed_ae_loc(page, {
            allow_access: true,
            authenticated_access: true,
            trusted_access: true,
            administrator_access: true,
        });

        await page.goto(config_url);

        // Heading confirms page rendered
        await expect(page.locator('h1:has-text("Leads Config")')).toBeVisible({ timeout: 10_000 });

        // Both collapsible sections are visible (default open)
        // Use the checkbox label text — it's unique inside the Payment section
        await expect(page.locator('text=Require Payment (Stripe)')).toBeVisible({ timeout: 5_000 });
        await expect(page.locator('text=Stripe Keys')).toBeVisible({ timeout: 5_000 });

        // Save button is present — two exist (header + bottom), check the header one
        await expect(page.locator('button:has-text("Save")').first()).toBeVisible({ timeout: 5_000 });
    });

    // -----------------------------------------------------------------------
    // 3. Save button disabled until form is dirty
    //
    // On load, is_dirty = false because draft JSON === initial JSON.
    // After toggling the "Require Payment" checkbox, is_dirty = true and the
    // Save button becomes enabled.
    // -----------------------------------------------------------------------
    test('save button disabled by default, enabled after checkbox change', async ({ page }) => {
        page.on('pageerror', (err) => console.error(`BROWSER ERROR: ${err.message}`));
        // Start with leads_require_payment: false so toggling it makes a real change
        await setup_config_routes(page, {
            event_data_overrides: {
                mod_exhibits_json: { leads_require_payment: false },
            },
        });
        await seed_ae_loc(page, {
            allow_access: true,
            authenticated_access: true,
            trusted_access: true,
            administrator_access: true,
        });

        await page.goto(config_url);

        // Wait for event to load and draft to initialize
        await page.waitForResponse(
            (r) => r.url().includes(`crud/event/${event_id}`) && r.status() === 200,
            { timeout: 8_000 }
        );

        // Wait for the form to render (draft_initialized)
        await expect(
            page.locator('input[type="checkbox"]').first()
        ).toBeVisible({ timeout: 8_000 });

        // Save must be disabled (not dirty yet)
        await expect(page.locator('button:has-text("Save Config")')).toBeDisabled({ timeout: 3_000 });

        // Toggle the "Require Payment" checkbox
        await page.locator('input[type="checkbox"]').first().click();

        // Save must now be enabled
        await expect(page.locator('button:has-text("Save Config")')).toBeEnabled({ timeout: 3_000 });
    });

    // -----------------------------------------------------------------------
    // 4. Save sends PATCH with mod_exhibits_json and shows "Saved" badge
    //
    // Clicking the Save button must PATCH event.mod_exhibits_json via the V3
    // API. The "Saved" badge must appear confirming success.
    // -----------------------------------------------------------------------
    test('save sends PATCH to event and shows Saved badge', async ({ page }) => {
        page.on('pageerror', (err) => console.error(`BROWSER ERROR: ${err.message}`));

        let patched_body: any = null;
        await setup_config_routes(page, {
            event_data_overrides: {
                mod_exhibits_json: { leads_require_payment: false },
            },
            on_patch: (body) => {
                patched_body = body;
            },
        });
        await seed_ae_loc(page, {
            allow_access: true,
            authenticated_access: true,
            trusted_access: true,
            administrator_access: true,
        });

        await page.goto(config_url);

        // Wait for form to initialize
        await page.waitForResponse(
            (r) => r.url().includes(`crud/event/${event_id}`) && r.status() === 200,
            { timeout: 8_000 }
        );
        await expect(
            page.locator('input[type="checkbox"]').first()
        ).toBeVisible({ timeout: 8_000 });

        // Make a change to unlock save
        await page.locator('input[type="checkbox"]').first().click();

        // Intercept the PATCH before clicking save
        const patch_promise = page.waitForRequest(
            (r) =>
                r.url().includes(`crud/event/${event_id}`) &&
                r.method() === 'PATCH',
            { timeout: 5_000 }
        );

        await page.locator('button:has-text("Save Config")').click();

        // PATCH must have been made
        await patch_promise;

        // "Saved" badge must appear
        await expect(page.locator('text=Saved')).toBeVisible({ timeout: 5_000 });

        // The patched body must include mod_exhibits_json.
        // update_ae_obj auto-serializes *_json fields to strings before the PATCH,
        // so parse it back to an object before asserting field values.
        expect(patched_body?.mod_exhibits_json).toBeDefined();
        const patched_cfg =
            typeof patched_body.mod_exhibits_json === 'string'
                ? JSON.parse(patched_body.mod_exhibits_json)
                : patched_body.mod_exhibits_json;
        expect(patched_cfg?.leads_require_payment).toBe(true);
    });
});
