// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import { defineConfig, devices } from "@playwright/experimental-ct-react";
import path from "path";
import * as dotenv from "dotenv";
import { createRequire } from "node:module";
import url from "node:url";
import { execSync } from "child_process";
// @ts-ignore -- sh_perspective.mjs is plain JS without type declarations
import { get_scope } from "../scripts/sh_perspective.mjs";

const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

dotenv.config({ path: "./.perspectiverc", quiet: true });

Error.stackTraceLimit = Infinity;

// TODO Don't hardcode this. AFAICT this can only be accomplished by choosing
// the port before calling the playwright CLI, via env var.
const TEST_SERVER_PORT = 6598;

const RUN_JUPYTERLAB = !!process.env.PSP_JUPYTERLAB_TESTS;

// TODO use this from core
const package_venn = (get_scope() as string[]).reduce(
    (acc: { include: string[]; exclude: string[] }, x: string) => {
        if (x.startsWith("!")) {
            acc.exclude.push(x);
        } else {
            acc.include.push(x);
        }

        return acc;
    },
    { include: [] as string[], exclude: [] as string[] },
);

let PACKAGE: string[] = [];
if (package_venn.include.length === 0) {
    PACKAGE = (
        JSON.parse(execSync(`pnpm m ls --json --depth=-1`).toString()) as {
            name?: string;
        }[]
    )
        .filter((x): x is { name: string } => x.name !== undefined)
        .map((x) => x.name.replace("@perspective-dev/", ""))
        .filter((x: string) => package_venn.exclude.indexOf(`!${x}`) === -1);
} else {
    PACKAGE = package_venn.include.filter(
        (x: string) => package_venn.exclude.indexOf(`!${x}`) === -1,
    );
}

const DEVICE_OPTIONS = {
    "Desktop Chrome": {
        // Lock DPR to 1 so WebGL viewport / canvas backing sizes are
        // identical across host machines (matters for pixel snapshots).
        // This also overrides the `devices["Desktop Chrome"]` default of 2.
        deviceScaleFactor: 1,
        launchOptions: {
            args: [
                // "--disable-accelerated-2d-canvas",
                // "--disable-gpu",
                "--no-sandbox",
                "--disable-setuid-sandbox",
                "--disable-dev-shm-usage",
                "--font-render-hinting=none",
                '--proxy-server="direct://"',
                "--proxy-bypass-list=*",
                "--js-flags=--expose-gc",
                "--enable-precise-memory-info",
                // Use the software WebGL backend so GPU/driver differences
                // between machines don't leak into snapshot pixels. The
                // 2× perf hit is under budget for the test suite.
                "--use-gl=swiftshader",
                "--use-angle=swiftshader",
                "--force-color-profile=srgb",
                "--disable-lcd-text",
            ],
        },
    },
};

const BROWSER_PACKAGES = [
    {
        packageName: "viewer",
        testDir: "rust/perspective-viewer/test/js",
    },
    {
        packageName: "react",
        testDir: "packages/react/test/js",
    },
    {
        packageName: "viewer-datagrid",
        testDir: "packages/viewer-datagrid/test/js",
    },
    {
        packageName: "viewer-charts",
        testDir: "packages/viewer-charts/test/ts",
    },
    {
        packageName: "jupyterlab",
        testDir: "packages/jupyterlab/test/js",
    },
    {
        packageName: "workspace",
        testDir: "packages/workspace/test/js",
    },
    {
        packageName: "docs",
        testDir: "docs/test/js",
    },
];

const NODE_PACKAGES = [
    {
        packageName: "client",
        testDir: "rust/perspective-js/test/js",
    },
];

const BROWSER_AND_PYTHON_PACKAGES = [
    {
        packageName: "python-perspective-jupyterlab",
        testDir: "packages/jupyterlab/test/jupyter",
    },
];

type ProjectConfig = NonNullable<
    Parameters<typeof defineConfig>[0]["projects"]
>[number];

let PROJECTS = (() => {
    const acc: ProjectConfig[] = [];
    if (RUN_JUPYTERLAB) {
        for (const pkg of BROWSER_AND_PYTHON_PACKAGES) {
            for (const device of Object.keys(
                DEVICE_OPTIONS,
            ) as (keyof typeof DEVICE_OPTIONS)[]) {
                acc.push({
                    name: `${pkg.packageName}-${device
                        .toLowerCase()
                        .replace(" ", "-")}`,
                    testDir: path.join(__dirname, "../../", pkg.testDir),
                    use: {
                        ...devices[device],
                        // baseURL: `http://localhost:${TEST_SERVER_PORT}`,
                        timezoneId: "UTC",
                        launchOptions: {
                            args: ["--js-flags=--expose-gc"],
                        },
                    },
                });
            }
        }
    } else {
        for (const pkg of NODE_PACKAGES) {
            if (PACKAGE == undefined || PACKAGE.includes(pkg.packageName)) {
                acc.push({
                    name: `${pkg.packageName}-node`,
                    testDir: path.join(__dirname, "../../", pkg.testDir),
                });
            }
        }

        for (const pkg of BROWSER_PACKAGES) {
            if (PACKAGE == undefined || PACKAGE.includes(pkg.packageName)) {
                for (const device of Object.keys(
                    DEVICE_OPTIONS,
                ) as (keyof typeof DEVICE_OPTIONS)[]) {
                    acc.push({
                        name: `${pkg.packageName}-${device
                            .toLowerCase()
                            .replace(" ", "-")}`,
                        testDir: path.join(__dirname, "../../", pkg.testDir),
                        use: {
                            ...devices[device],
                            ...DEVICE_OPTIONS[device],
                            baseURL: `http://localhost:${TEST_SERVER_PORT}`,
                            timezoneId: "UTC",
                        },
                    });
                }
            }
        }
    }

    return acc;
})();

const __require = createRequire(import.meta.url);

const GLOBAL_SETUP_PATH = __require.resolve(
    "@perspective-dev/jupyterlab/test/config/jupyter/globalSetup.ts",
);

const GLOBAL_TEARDOWN_PATH = __require.resolve(
    "@perspective-dev/jupyterlab/test/config/jupyter/globalTeardown.ts",
);

// See https://playwright.dev/docs/test-configuration.
export default defineConfig({
    timeout: 30_000,
    expect: {
        timeout: 30_000,
    },
    repeatEach: process.env.PSP_SATURATE
        ? parseInt(process.env.PSP_SATURATE)
        : 0,
    forbidOnly: !!process.env.CI,
    workers: process.env.PSP_DEBUG ? 1 : "50%",
    retries: 0,
    quiet: !process.env.PSP_DEBUG,
    reporter: process.env.CI ? [["github"], ["html"]] : [["dot"]],
    projects: PROJECTS,
    outputDir: "dist/results",
    use: {
        launchOptions: {
            slowMo: 500, // 500 milliseconds delay
        },
        headless: !process.env.PSP_HEADED,
        ctPort: 3100,
        viewport: { width: 1280, height: 720 },
        actionTimeout: 0,
        // trace: "retain-on-failure",
        // screenshot: "only-on-failure",
        // video: "retain-on-failure",
    },
    updateSnapshots: "none",
    globalSetup: RUN_JUPYTERLAB
        ? GLOBAL_SETUP_PATH
        : path.join(__dirname, "src/js/global_startup.ts"),
    globalTeardown: RUN_JUPYTERLAB
        ? GLOBAL_TEARDOWN_PATH
        : path.join(__dirname, "src/js/global_teardown.ts"),
    snapshotPathTemplate:
        "dist/snapshots/{projectName}/{testFilePath}/{arg}{ext}",
    webServer: {
        command: "tsx src/js/start_test_server.ts",
        port: TEST_SERVER_PORT,
        reuseExistingServer: true,
        stdout: "pipe",
        stderr: "pipe",
    },
});
