/**
 * tests/_helpers/leads_helpers.ts
 *
 * Playwright test helpers for the Exhibitor Leads module.
 *
 * Provides:
 *   - Demo ID constants
 *   - Data factories (exhibit, tracking, event-with-leads)
 *   - localStorage seeders (ae_loc, ae_events_loc)
 *   - V3 API route mocks for the exhibit page
 *   - One-call beforeEach helper: setup_leads_test_page()
 *
 * Auth model:
 *   ae_loc.manager_access = true   → bypasses sign-in entirely (admin shortcut)
 *   ae_events_loc.leads.auth_exhibit_kv[exhibit_id]  → exhibit-level auth
 *     { key: passcode_or_email, type: 'shared' | 'licensed', updated_on }
 *
 * Multiple exhibits can be authorized simultaneously — auth_exhibit_kv is keyed
 * by exhibit_id, so signing into booth A and booth B are independent entries.
 *
 * API routes mocked (all matching pattern `v3/...`):
 *   GET  /v3/crud/event_exhibit/{exhibit_id}            → exhibit data (loads into Dexie)
 *   GET  /v3/crud/event/{event_id}                      → event data (mod_exhibits_json)
 *   POST site_domain/search                             → mock site domain
 *   POST exhibit_tracking/search                        → tracking list (empty by default)
 *   any  everything else                                → { data: [] }
 */

import type { Page } from '@playwright/test';
import { mock_site_domain, testing_account_id, testing_event_id } from './env';
import { ae_app_local_data_defaults } from './ae_defaults';

// ---------------------------------------------------------------------------
// Demo IDs — real demo-DB rows under event 'pjrcghqwert' (1) "Demo One Sky IT Conference"
// IDs are documented in tests/README.md under "Events Modules".
// ---------------------------------------------------------------------------
/** Primary demo exhibit — (1) "One Sky's Awesome Exhibit". */
export const testing_exhibit_id = 'xK_9yEj1bQY';
/** Secondary demo exhibit — (3) "Exhibit for Precon Events". Used in multi-exhibit auth tests. */
export const testing_exhibit_id_b = 'acHCkrCDaYs';
/** Shared staff passcode used in mocked API responses (not a real DB value). */
export const exhibit_staff_passcode = 'BOOTH2026';

// ---------------------------------------------------------------------------
// Data factories
// ---------------------------------------------------------------------------

/** Minimal event object with mod_exhibits_json set for Leads. */
export const minimal_event_for_leads = (
    event_id: string,
    overrides: Record<string, any> = {}
) => ({
    data: {
        id: event_id,
        event_id: event_id,
        name: 'Test Event (leads)',
        cfg_json: {},
        mod_pres_mgmt_json: {},
        mod_badges_json: {},
        mod_abstracts_json: {},
        mod_exhibits_json: {
            leads_require_payment: true,
            stripe_publishable_key: null,
            stripe_btn_1_license: null,
            stripe_btn_3_license: null,
            stripe_btn_6_license: null,
            stripe_btn_10_license: null,
        },
        mod_meetings_json: {},
        ...overrides,
    },
});

/**
 * Minimal exhibit object.
 *
 * Includes staff_passcode so the sign-in form can validate against it.
 * The API response is wrapped in { data: ... } to match V3 format.
 */
export const minimal_exhibit = (
    exhibit_id: string,
    overrides: Record<string, any> = {}
) => ({
    data: {
        id: exhibit_id,
        event_exhibit_id: exhibit_id,
        event_id: testing_event_id,
        name: 'Test Booth — ACME Corp',
        code: 'ACME',
        staff_passcode: exhibit_staff_passcode,
        license_max: 3,
        license_li_json: '[]',
        leads_api_access: true,
        leads_custom_questions_json: '[]',
        leads_device_sm_qty: 0,
        leads_device_lg_qty: 0,
        enable: '1',
        hide: '0',
        priority: '0',
        sort: '0',
        group: null,
        notes: null,
        cfg_json: null,
        data_json: null,
        created_on: '2026-01-01T00:00:00',
        updated_on: '2026-01-01T00:00:00',
        ...overrides,
    },
});

/**
 * Minimal event badge for use in badge search results.
 *
 * allow_tracking MUST be true for the "Add" button to appear.
 * Uses the real demo badge ID from tests/README.md.
 */
export const minimal_badge = (overrides: Record<string, any> = {}) => ({
    event_badge_id: 'UIJT-73-63-61',           // demo badge (37163) "Scott Idem"
    event_badge_id_random: 'UIJT-73-63-61',
    full_name: 'Scott Idem',
    email: 'scott@demo.oneskyit.com',
    affiliations: 'One Sky IT',
    allow_tracking: true,                        // required for Add button to render
    badge_type: 'attendee',
    ...overrides,
});

/** Minimal exhibit tracking record (a captured lead). */
export const minimal_tracking = (
    exhibit_id: string,
    tracking_id: string,
    overrides: Record<string, any> = {}
) => ({
    id: tracking_id,
    event_exhibit_tracking_id: tracking_id,
    event_exhibit_id: exhibit_id,
    event_badge_id: 'BADGE001',
    event_badge_full_name: 'Jane Doe',
    event_badge_email: 'jane@example.com',
    external_person_id: 'shared_passcode', // shared booth staff capture identity
    exhibitor_notes: '',
    enable: 1,
    hide: false,
    created_on: new Date().toISOString(),
    updated_on: new Date().toISOString(),
    ...overrides,
});

// ---------------------------------------------------------------------------
// localStorage seeders
// ---------------------------------------------------------------------------

/**
 * Seed ae_loc with the correct __version (2 = AE_LOC_VERSION) and optional
 * access flag overrides.
 *
 * Must be called via addInitScript so it runs before store_versions.ts wipes
 * stale data. If __version doesn't match AE_LOC_VERSION, the store is wiped
 * and auth state is lost — this helper ensures version is always correct.
 *
 * Common access overrides:
 *   { manager_access: true }   → bypasses Leads sign-in (admin shortcut)
 *   { trusted_access: true }   → triggers passcode auto-fill in sign-in form
 */
export async function seed_ae_loc(
    page: Page,
    overrides: Record<string, any> = {}
): Promise<void> {
    await page.addInitScript(
        ([defaults, ovrd]: [typeof ae_app_local_data_defaults, Record<string, any>]) => {
            const data = { ...defaults, ...ovrd, __version: 2 };
            window.localStorage.setItem('ae_loc', JSON.stringify(data));
        },
        [ae_app_local_data_defaults, overrides] as [
            typeof ae_app_local_data_defaults,
            Record<string, any>
        ]
    );
}

/**
 * Seed ae_leads_loc (new Svelte-5 PersistedState store) with defaults and __version.
 * Must be called via addInitScript so it runs before store_versions.ts wipes stale data.
 */
export async function seed_leads_loc(
    page: Page,
    overrides: Record<string, any> = {}
): Promise<void> {
    await page.addInitScript(([ovrd]: [Record<string, any>]) => {
        const defaults = {
            __version: 1,
            show_option__paid_tab: true,
            show_content__scan_alert: true,
            show_content__scan_requirements: true,
            show_content__custom_question_descriptions: true,
            show_content__email_link_warning: true,
            default_to_scan: true,
            default__external_registration_id: '2024_Annual Meeting',
            auto_view: true,
            auto_hide_on_sign_in: true,
            show_hidden: false,
            show_not_enabled: false,
            refresh_interval__tracking_li: 30000,
            refresh_interval_sec: 25,
            search_version: 0,
            qry__remote_first: false,
            qry__search_text: '',
            qry__sort_order: 'name_asc',
            tracking__search_version: 0,
            tracking__qry__remote_first: false,
            tracking__qry__search_text: '',
            tracking__qry__sort_order: 'created_desc',
            tracking__qry__licensee_email: 'all',
            entered_passcode: null,
            auth_exhibit_kv: {},
            edit_license_li: false,
            tab: {},
            tab_add_mode: {},
            tab_scan_qualify: {}
        };
        const data = { ...defaults, ...ovrd, __version: 1 };
        window.localStorage.setItem('ae_leads_loc', JSON.stringify(data));
    }, [overrides] as [Record<string, any>]);
}

/**
 * Seed ae_events_loc with the correct __version (1 = AE_EVENTS_LOC_VERSION)
 * and the given per-exhibit auth entries.
 *
 * auth_kv shape: { [exhibit_id]: { key: string; type: 'shared' | 'licensed' } }
 *
 * Multiple exhibit IDs can be passed simultaneously — each becomes an
 * independent entry in auth_exhibit_kv, so a user can be signed into
 * Booth A and Booth B at the same time (separate auth scopes).
 *
 * An empty auth_kv (default) means no exhibits are authorized → sign-in
 * form is shown.
 *
 * @param leads_overrides  Extra fields merged into the `leads` namespace, e.g.
 *   `{ tab_add_mode: { [exhibit_id]: 'search' } }` to start in manual search mode.
 */
export async function seed_events_loc(
    page: Page,
    auth_kv: Record<string, { key: string; type: string }> = {},
    leads_overrides: Record<string, any> = {}
): Promise<void> {
    await page.addInitScript(
        ([kv, lo]: [Record<string, { key: string; type: string }>, Record<string, any>]) => {
        // Build auth_exhibit_kv with timestamps added server-side
        const auth_exhibit_kv: Record<
            string,
            { key: string; type: string; updated_on: string }
        > = {};
        for (const [eid, entry] of Object.entries(kv)) {
            auth_exhibit_kv[eid] = { ...entry, updated_on: new Date().toISOString() };
        }

        const events_loc = {
            __version: 1, // Must match AE_EVENTS_LOC_VERSION in store_versions.ts

            // Deployment stamp — must match events_sess.ver to avoid "new version available" banner.
            ver: '2025-10-16_2139',

            name: 'Aether - Events',
            title: "OSIT's Æ Events",
            ds: {},
            events_cfg_json: {},
            event_id: null,
            qry__enabled: 'enabled',
            qry__hidden: 'not_hidden',
            qry__limit: 20,
            qry__offset: 0,
            show_details: false,
            auth__person: {},
            auth__kv: {
                event: {},
                exhibit: {},
                location: {},
                session: {},
                presentation: {},
                presenter: {},
                person: {},
            },
            // Non-leads modules: empty — tests only exercise leads routes
            badges: {},
            launcher: {},
            pres_mgmt: {},

            leads: {
                // Leads loc defaults (inlined — avoids SvelteKit module resolution in Node test runner)
                show_option__paid_tab: true,
                show_content__scan_alert: true,
                show_content__scan_requirements: true,
                show_content__custom_question_descriptions: true,
                show_content__email_link_warning: true,
                default_to_scan: true,
                default__external_registration_id: '2024_Annual Meeting',
                auto_view: true,
                auto_hide_on_sign_in: true,
                show_hidden: false,
                show_not_enabled: false,
                refresh_interval__tracking_li: 30000,
                search_version: 0,
                qry__remote_first: false,
                qry__search_text: '',
                qry__sort_order: 'name_asc',
                tracking__search_version: 0,
                tracking__qry__remote_first: false,
                tracking__qry__search_text: '',
                tracking__qry__sort_order: 'created_desc',
                tracking__qry__licensee_email: 'all',
                entered_passcode: null,
                edit_license_li: false,
                tab: {},
                tab_add_mode: {},
                tab_scan_qualify: {},
                // The auth entries for this test session
                auth_exhibit_kv,
                // Any extra leads fields (e.g. tab_add_mode, tab_scan_qualify)
                ...lo,
            },
        };

        window.localStorage.setItem('ae_events_loc', JSON.stringify(events_loc));
    }, [auth_kv, leads_overrides] as [Record<string, { key: string; type: string }>, Record<string, any>]);
}

// ---------------------------------------------------------------------------
// Route mocks
// ---------------------------------------------------------------------------

/**
 * Attach V3 API route mocks for the exhibit page to the given Playwright page.
 *
 * Intercepts all requests matching the `v3/` pattern and fulfills them with minimal mock data so
 * the app can initialize (site_domain → ae_loc hydration, event → event record,
 * event_exhibit → exhibit record written into Dexie for sign-in form).
 *
 * @param tracking_li       Pre-existing leads to include in tracking/search responses.
 * @param badge_li          Badges returned by event_badge/search. Defaults to one badge
 *                          with allow_tracking=true (the demo "Scott Idem" badge).
 * @param event_data_overrides  Merged into the `data` object of the event response.
 *                              Use to override mod_exhibits_json, e.g.
 *                              `{ mod_exhibits_json: { leads_require_payment: false } }`.
 */
export async function attach_leads_routes(
    page: Page,
    event_id: string,
    exhibit_id: string,
    opts: {
        staff_passcode?: string;
        exhibit_name?: string;
        exhibit_overrides?: Record<string, unknown>;
        tracking_li?: any[];
        badge_li?: any[];
        event_data_overrides?: Record<string, any>;
    } = {}
): Promise<void> {
    const {
        staff_passcode = exhibit_staff_passcode,
        exhibit_name = 'Test Booth — ACME Corp',
        exhibit_overrides = {} as Record<string, unknown>,
        tracking_li = [],
        badge_li = [minimal_badge()],
        event_data_overrides = {},
    } = opts;

    // In-memory state for newly created tracking records within this session
    let created_tracking: any = null;

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

        // Site domain init — required for ae_loc hydration in the root layout
        if (url.includes('site_domain/search')) {
            return route.fulfill({
                status: 200,
                contentType: 'application/json',
                body: JSON.stringify({ data: [mock_site_domain] }),
            });
        }

        // Exhibit record — loaded by +layout.ts into Dexie so the sign-in form
        // can check $lq__exhibit_obj.staff_passcode
        if (url.includes(`/v3/crud/event_exhibit/${exhibit_id}`) && method === 'GET') {
            return route.fulfill({
                status: 200,
                contentType: 'application/json',
                body: JSON.stringify(
                    minimal_exhibit(exhibit_id, { staff_passcode, name: exhibit_name, ...exhibit_overrides })
                ),
            });
        }

        // Event record — needed for mod_exhibits_json (leads_require_payment, Stripe config)
        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)),
            });
        }

        // Badge search — returns badge_li for any event_badge search
        // URL: POST /v3/crud/event/{event_id}/event_badge/search
        if (url.includes('event_badge') && url.includes('search') && method === 'POST') {
            return route.fulfill({
                status: 200,
                contentType: 'application/json',
                body: JSON.stringify({ data: badge_li }),
            });
        }

        // Tracking create — POST /v3/crud/event_exhibit/{exhibit_id}/event_exhibit_tracking/
        // Returns a new tracking record; also adds it to the in-session list so subsequent
        // tracking searches include it.
        if (
            url.includes(`event_exhibit/${exhibit_id}/event_exhibit_tracking`) &&
            method === 'POST'
        ) {
            const post = await req.postData();
            const body = post ? JSON.parse(post) : {};
            created_tracking = {
                id: 'TRK-TEST-001',
                event_exhibit_tracking_id: 'TRK-TEST-001',
                event_exhibit_tracking_id_random: 'TRK-TEST-001',
                event_exhibit_id: exhibit_id,
                event_badge_id: body.event_badge_id ?? 'UIJT-73-63-61',
                event_badge_full_name: 'Scott Idem',
                event_badge_email: 'scott@demo.oneskyit.com',
                external_person_id: body.external_person_id ?? 'shared_passcode',
                group: body.group ?? 'shared_passcode',
                exhibitor_notes: '',
                enable: 1,
                hide: false,
                created_on: new Date().toISOString(),
                updated_on: new Date().toISOString(),
            };
            return route.fulfill({
                status: 201,
                contentType: 'application/json',
                body: JSON.stringify({ data: created_tracking }),
            });
        }

        // Tracking search — returns pre-populated list + any newly created record
        if (url.includes('exhibit_tracking') && url.includes('search') && method === 'POST') {
            const all = created_tracking
                ? [...tracking_li, created_tracking]
                : tracking_li;
            return route.fulfill({
                status: 200,
                contentType: 'application/json',
                body: JSON.stringify({ data: all }),
            });
        }

        // Fallback: return empty success for any other V3 request (tracking list, etc.)
        return route.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({ data: [] }),
        });
    });
}

// ---------------------------------------------------------------------------
// One-call beforeEach
// ---------------------------------------------------------------------------

/**
 * Canonical beforeEach helper for leads exhibit page tests.
 *
 * Wires up in order (order matters — addInitScript runs in insertion order):
 *   1. page.on('pageerror') → stderr
 *   2. attach_leads_routes() — intercept all /v3/ API calls
 *   3. seed_ae_loc()         — seed ae_loc (correct __version + access flags)
 *   4. seed_events_loc()     — seed ae_events_loc (optional exhibit auth entries)
 *
 * @param access         ae_loc overrides e.g. { manager_access: true }
 * @param auth_kv        Per-exhibit auth — pass {} for no auth (shows sign-in form)
 * @param leads_overrides  Extra fields merged into ae_events_loc.leads namespace,
 *                         e.g. { tab_add_mode: { [exhibit_id]: 'search' } }
 */
export async function setup_leads_test_page(
    page: Page,
    event_id: string,
    exhibit_id: string,
    opts: {
        access?: Record<string, any>;
        auth_kv?: Record<string, { key: string; type: string }>;
        leads_overrides?: Record<string, any>;
        staff_passcode?: string;
        exhibit_name?: string;
        exhibit_overrides?: Record<string, unknown>;
        tracking_li?: any[];
        badge_li?: any[];
        event_data_overrides?: Record<string, any>;
    } = {}
): Promise<void> {
    const { access = {}, auth_kv = {}, leads_overrides = {}, ...route_opts } = opts;

    // Build auth_exhibit_kv with timestamps for ae_leads_loc (the current store).
    // seed_events_loc also seeds the old ae_events_loc for backwards compatibility,
    // but is_signed_in now reads from leads_loc (ae_leads_loc) exclusively.
    const auth_exhibit_kv_with_ts: Record<string, { key: string; type: string; updated_on: string }> = {};
    for (const [eid, entry] of Object.entries(auth_kv)) {
        auth_exhibit_kv_with_ts[eid] = { ...entry, updated_on: new Date().toISOString() };
    }

    page.on('pageerror', (err) => console.error(`BROWSER ERROR: ${err.message}`));
    await attach_leads_routes(page, event_id, exhibit_id, route_opts);
    await seed_ae_loc(page, access);
    await seed_leads_loc(page, { ...leads_overrides, auth_exhibit_kv: auth_exhibit_kv_with_ts });
    await seed_events_loc(page, auth_kv, leads_overrides);
}

export default {
    testing_exhibit_id,
    testing_exhibit_id_b,
    exhibit_staff_passcode,
    minimal_event_for_leads,
    minimal_exhibit,
    minimal_badge,
    minimal_tracking,
    seed_ae_loc,
    seed_leads_loc,
    seed_events_loc,
    attach_leads_routes,
    setup_leads_test_page,
};
