import { test as base, expect, type Page } from "@playwright/test"
import { ensureTestUser, type TestUserCredentials } from "../api/helpers/test-user"

const API_BASE = "/api"

/**
 * Shared E2E test fixtures.
 *
 * Worker-scoped:
 * - `testUser` – persistent test user (created/reused once per worker)
 *
 * Test-scoped:
 * - `authedPage` – Page with logged-in user (token injection via sessionStorage)
 * - `accessToken` – raw JWT access token
 */
type E2eWorkerFixtures = {
    testUser: TestUserCredentials
}

type E2eFixtures = {
    authedPage: Page
    accessToken: string
}

export const test = base.extend<E2eFixtures, E2eWorkerFixtures>({
    /**
     * Override page fixture: BrowserSync keeps a WebSocket open permanently,
     * preventing "load" and "networkidle" from resolving. We default all
     * navigation methods to "domcontentloaded".
     */
    page: async ({ page }, use) => {
        const origGoto = page.goto.bind(page)
        const origReload = page.reload.bind(page)
        const origGoBack = page.goBack.bind(page)
        const origGoForward = page.goForward.bind(page)

        page.goto = ((url: string, opts?: any) =>
            origGoto(url, { waitUntil: "domcontentloaded", ...opts })) as typeof page.goto
        page.reload = ((opts?: any) => origReload({ waitUntil: "domcontentloaded", ...opts })) as typeof page.reload
        page.goBack = ((opts?: any) => origGoBack({ waitUntil: "domcontentloaded", ...opts })) as typeof page.goBack
        page.goForward = ((opts?: any) =>
            origGoForward({ waitUntil: "domcontentloaded", ...opts })) as typeof page.goForward

        await use(page)
    },

    // Worker-scoped: create/reuse test user once per worker
    testUser: [
        async ({ playwright }, use) => {
            const baseURL = process.env.CODING_URL || "https://localhost:3000"
            const user = await ensureTestUser(baseURL)
            await use(user)
        },
        { scope: "worker" },
    ],

    accessToken: async ({ testUser }, use) => {
        await use(testUser.accessToken)
    },

    // Test-scoped: Page with logged-in user via sessionStorage token injection
    authedPage: async ({ page, testUser, baseURL }, use) => {
        // Navigate to home so domain is set for sessionStorage
        await page.goto("/", { waitUntil: "domcontentloaded" })
        await page.waitForLoadState("domcontentloaded")

        // Inject auth token into sessionStorage (adapt key names to your app)
        await page.evaluate(
            ({ token, user }) => {
                sessionStorage.setItem("auth_token", token)
                sessionStorage.setItem(
                    "auth_user",
                    JSON.stringify({
                        email: user.email,
                        firstName: user.firstName,
                        lastName: user.lastName,
                    })
                )
            },
            {
                token: testUser.accessToken,
                user: testUser,
            }
        )

        // Reload so the app reads the token from sessionStorage
        await page.reload({ waitUntil: "domcontentloaded" })
        await page.waitForLoadState("domcontentloaded")
        await expect(page.locator("#appContainer")).not.toBeEmpty({ timeout: 15000 })

        await use(page)
    },
})

// ── Helpers ──────────────────────────────────────────────────────────────────

/**
 * Wait for the SPA to be ready (appContainer rendered).
 * Returns the detected language prefix from the URL.
 */
export async function waitForSpaReady(page: Page): Promise<string> {
    await page.waitForLoadState("domcontentloaded")
    await expect(page.locator("#appContainer")).not.toBeEmpty({ timeout: 15000 })
    const url = page.url()
    const match = url.match(/\/([a-z]{2})(\/|$)/)
    return match?.[1] || "de"
}

/**
 * Navigate to a route, prepending the current language prefix.
 */
export async function navigateToRoute(page: Page, routePath: string): Promise<void> {
    const url = page.url()
    const match = url.match(/\/([a-z]{2})(\/|$)/)
    const lang = match?.[1] || "de"
    const fullPath = routePath === "/" ? `/${lang}` : `/${lang}${routePath}`
    await page.goto(fullPath)
    await page.waitForLoadState("domcontentloaded")
    await expect(page.locator("#appContainer")).not.toBeEmpty({ timeout: 15000 })
}

/**
 * Click an SPA link and verify no full page reload occurred.
 */
export async function clickSpaLink(page: Page, linkSelector: string): Promise<void> {
    await page.evaluate(() => {
        ;(window as any).__spa_navigation_marker = true
    })
    await page.locator(linkSelector).first().click()
    await page.waitForLoadState("domcontentloaded")
    const markerExists = await page.evaluate(() => {
        return (window as any).__spa_navigation_marker === true
    })
    if (!markerExists) {
        throw new Error(`SPA navigation failed: full page reload detected when clicking "${linkSelector}"`)
    }
}

export { expect, API_BASE, type Page }
