// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ 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 {
    test,
    expect,
    PageView,
    compareContentsToSnapshot,
} from "../helpers.ts";

async function openSidebarAndScrollToBottom() {
    const elem = document.querySelector("perspective-viewer");
    await elem.getTable();
    await elem.toggleConfig(true);
    elem.shadowRoot.querySelector("#active-columns").scrollTop = 500;
}

async function checkSaveDisabled(page, expr) {
    let view = new PageView(page);
    let settingsPanel = await view.openSettingsPanel();
    await settingsPanel.createNewExpression("", expr, false);
}

test.beforeEach(async ({ page }) => {
    await page.goto(
        "/node_modules/@perspective-dev/viewer/test/html/superstore.html",
    );
    await page.evaluate(async () => {
        while (!window["__TEST_PERSPECTIVE_READY__"]) {
            await new Promise((x) => setTimeout(x, 10));
        }
    });

    await page.evaluate(async () => {
        await document.querySelector("perspective-viewer").restore({
            plugin: "Debug",
        });
    });
});

test.describe("Expressions", () => {
    test("editor > opens on add-column button click", async ({ page }) => {
        await page.evaluate(openSidebarAndScrollToBottom);

        await page.waitForFunction(
            () =>
                !!document
                    .querySelector("perspective-viewer")
                    .shadowRoot.querySelector("#add-expression"),
        );

        await page.locator("perspective-viewer #add-expression").click();

        await page.waitForFunction(() => {
            const root = document
                .querySelector("perspective-viewer")
                .shadowRoot.querySelector("#editor-container");

            return !!root;
        });

        const editor = await page.waitForFunction(async () => {
            const root = document
                .querySelector("perspective-viewer")
                .shadowRoot.querySelector("#editor-container");
            return root.querySelector("#content");
        });

        const contents = await editor.evaluate((x) => x.outerHTML);

        await page.evaluate(() => document.activeElement.blur());

        await compareContentsToSnapshot(contents);
    });

    test("editor > closes on close button click", async ({ page }) => {
        await page.evaluate(openSidebarAndScrollToBottom);
        await page.waitForFunction(
            () =>
                !!document
                    .querySelector("perspective-viewer")
                    .shadowRoot.querySelector("#add-expression"),
        );

        await page.locator("perspective-viewer #add-expression").click();
        await page.waitForSelector("#editor-container");
        await page.evaluate(async () => {
            let root = document.querySelector("perspective-viewer").shadowRoot;
            await root.querySelector("#column_settings_close_button").click();
        });

        await page.waitForSelector("#editor-container", {
            state: "hidden",
        });

        const contents = await page.evaluate(async () => {
            let root = document.querySelector("perspective-viewer").shadowRoot;
            return (
                root.querySelector("#editor-container")?.innerHTML || "MISSING"
            );
        });

        await compareContentsToSnapshot(contents);
    });

    test("validation > disables save for unknown symbols", async ({ page }) => {
        await checkSaveDisabled(page, "abc");
    });

    test("validation > disables save for type-invalid expression", async ({
        page,
    }) => {
        await checkSaveDisabled(page, '"Sales" + "Category";');
    });

    test("validation > disables save for invalid column references", async ({
        page,
    }) => {
        await checkSaveDisabled(page, '"aaaa" + "Sales";');
    });

    test("columns > shows both aliased and non-aliased expressions", async ({
        page,
    }) => {
        const contents = await page.evaluate(async () => {
            document.activeElement.blur();
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                expressions: { "1 + 2": "1 + 2", abc: "3 + 4" },
            });
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });

    // No longer relevant as we cannot save a duplicate identifier
    test.skip("columns > overwrites a duplicate expression alias", async ({
        page,
    }) => {
        let view = new PageView(page);
        view.restore({ expressions: { "4 + 5": "3 + 4" } });

        await view.settingsPanel.createNewExpression("", "4 + 5", true);

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });

    // No longer relevant as we cannot save a duplicate identifier
    test.skip("columns > overwrites a duplicate expression", async ({
        page,
    }) => {
        let view = new PageView(page);
        view.restore({ expressions: { "3 + 4": "3 + 4" } });
        await view.settingsPanel.createNewExpression("", "3 + 4", true);

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });

    test("reset > deletes all expressions on full reset", async ({ page }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                expressions: { "3 + 4": "3 + 4", "1 + 2": "1 + 2" },
            });
            await elem.reset(true);
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return (
                elem.shadowRoot.querySelector("#sub-columns")?.innerHTML ||
                "MISSING"
            );
        });

        await compareContentsToSnapshot(contents);
    });

    test("reset > preserves expressions on partial reset", async ({ page }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                expressions: { "3 + 4": "3 + 4", "1 + 2": "1 + 2" },
            });
            await elem.reset(false);
        });

        const content = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(content);
    });

    test("reset > deletes expressions used in columns on full reset", async ({
        page,
    }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                columns: ["1 + 2"],
                expressions: { "3 + 4": "3 + 4", "1 + 2": "1 + 2" },
            });
            await elem.reset(true);
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return (
                elem.shadowRoot.querySelector("#sub-columns")?.innerHTML ||
                "MISSING"
            );
        });

        await compareContentsToSnapshot(contents);
    });

    test("reset > preserves expressions used in columns on partial reset", async ({
        page,
    }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                columns: ["1 + 2"],
                expressions: { "3 + 4": "3 + 4", "1 + 2": "1 + 2" },
            });
            await elem.reset(false);
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });

    test("reset > deletes expressions used in group_by/sort/filter on full reset", async ({
        page,
    }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                columns: ["1 + 2"],
                group_by: ["3 + 4"],
                sort: [["1 + 2", "asc"]],
                filter: [["1 + 2", "==", 3]],
                expressions: { "3 + 4": "3 + 4", "1 + 2": "1 + 2" },
            });
            await elem.reset(true);
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return (
                elem.shadowRoot.querySelector("#sub-columns")?.innerHTML ||
                "MISSING"
            );
        });

        await compareContentsToSnapshot(contents);
    });

    test("persistence > survives views that don't reference them", async ({
        page,
    }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                expressions: { "3 + 4": "3 + 4", "1 + 2": "1 + 2" },
            });
            await elem.restore({
                columns: ["State"],
            });
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });

    test("add button > marks on hover", async ({ page }) => {
        await page.evaluate(openSidebarAndScrollToBottom);
        let addExprButton = await page.waitForSelector("#add-expression");
        let notHovered = await addExprButton.getAttribute("class");
        expect(notHovered).toBeNull();

        await addExprButton.hover();
        let hovered = await addExprButton.getAttribute("class");
        expect(hovered).toBe("dragdrop-hover");
    });

    // Currently does not work in Firefox!
    test("add button > marks on click", async ({ page }) => {
        await page.evaluate(openSidebarAndScrollToBottom);
        let addExprButton = await page.waitForSelector("#add-expression");
        let unclicked = await addExprButton.getAttribute("class");
        expect(unclicked).toBeNull();
        await addExprButton.click();
        await page.evaluate(openSidebarAndScrollToBottom);
        let clicked = await addExprButton.getAttribute("class");
        expect(clicked).toBe("dragdrop-hover");
    });

    test("persistence > survives views that reference them", async ({
        page,
    }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                expressions: { "3 + 4": "3 + 4", "1 + 2": "1 + 2" },
            });
            await elem.restore({
                columns: ["3 + 4"],
            });
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });

    test("aggregates > applies aggregate to expression column", async ({
        page,
    }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                expressions: { '"Sales" + 100': '"Sales" + 100' },
                aggregates: { '"Sales" + 100': "avg" },
                group_by: ["State"],
                columns: ['"Sales" + 100'],
            });
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });

    test("sort > sorts by a hidden expression column", async ({ page }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                expressions: { '"Sales" + 100': '"Sales" + 100' },
                sort: [['"Sales" + 100', "asc"]],
                columns: ["Row ID"],
            });
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });

    test("filter > filters by an expression column", async ({ page }) => {
        await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            await elem.toggleConfig(true);
            await elem.restore({
                expressions: { '"Sales" + 100': '"Sales" + 100' },
                filter: [['"Sales" + 100', ">", 150]],
                columns: ["Row ID", '"Sales" + 100'],
            });
        });

        const contents = await page.evaluate(async () => {
            const elem = document.querySelector("perspective-viewer");
            return elem.shadowRoot.querySelector("#sub-columns").innerHTML;
        });

        await compareContentsToSnapshot(contents);
    });
});
