// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ 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 } from "@perspective-dev/test";
import perspective from "./perspective_client.ts";
import type * as psp_types from "@perspective-dev/client";

((perspective) => {
    test.describe("Clear", function () {
        test("removes the rows from the table", async function () {
            const table = await perspective.table([{ x: 1 }]);
            const view = await table.view();
            let json = await view.to_json();
            expect(json).toHaveLength(1);
            table.clear();
            json = await view.to_json();
            expect(json).toHaveLength(0);
            view.delete();
            table.delete();
        });

        test("to_columns output is empty", async function () {
            const table = await perspective.table([{ x: 1 }]);
            const view = await table.view();
            let result = await view.to_columns();
            expect(result).toEqual({
                x: [1],
            });
            table.clear();
            result = await view.to_columns();
            expect(result).toEqual({ x: [] });
            view.delete();
            table.delete();
        });
    });

    test.describe("Replace", function () {
        test("replaces the rows in the table with the input data", async function () {
            const table = await perspective.table([
                { x: 1, y: 2 },
                { x: 3, y: 4 },
            ]);
            const view = await table.view();
            let json = await view.to_json();
            expect(json).toHaveLength(2);
            expect(json).toEqual([
                { x: 1, y: 2 },
                { x: 3, y: 4 },
            ]);
            await table.replace([{ x: 5, y: 6 }]);
            json = await view.to_json();
            expect(json).toHaveLength(1);
            expect(json).toEqual([{ x: 5, y: 6 }]);
            view.delete();
            table.delete();
        });

        test("Replaces CSV Table with high precision datetimes", async function () {
            const a = '"start"\n2024-08-14T14:06:07.826Z';
            const b = '"start"\n2024-08-14T14:06:09.876667543Z';
            const table = await perspective.table(a);
            const view = await table.view();
            const csv1 = await view.to_csv();
            expect(csv1).toEqual('"start"\n2024-08-14 14:06:07.826\n');

            await table.replace(b);
            const csv2 = await view.to_csv();
            expect(csv2).toEqual('"start"\n2024-08-14 14:06:09.876\n');
            view.delete();
            table.delete();
        });

        test("replaces the rows in the table with the input data and fires an on_update", async function () {
            const table = await perspective.table([
                { x: 1, y: 2 },
                { x: 3, y: 4 },
            ]);

            const view = await table.view();

            let emit: ((x: unknown) => void) | undefined;
            const callback = async function (updated: psp_types.OnUpdateArgs) {
                expect(updated.port_id).toEqual(0);
                const json = await view.to_json();
                expect(json).toHaveLength(1);
                expect(json).toEqual([{ x: 5, y: 6 }]);
                view.delete();
                table.delete();
                emit!(undefined);
            };

            let result = new Promise((x) => {
                emit = x;
            });

            view.on_update(callback);
            let json = await view.to_json();
            expect(json).toHaveLength(2);
            expect(json).toEqual([
                { x: 1, y: 2 },
                { x: 3, y: 4 },
            ]);

            await table.replace([{ x: 5, y: 6 }]);
            await result;
        });

        test("replaces the rows in the table with the input data and fires an on_update with the correct delta", async function () {
            const table = await perspective.table([
                { x: 1, y: 2 },
                { x: 3, y: 4 },
            ]);

            const view = await table.view();
            let emit: ((x: unknown) => void) | undefined;
            const callback = async function (updated: psp_types.OnUpdateArgs) {
                expect(updated.port_id).toEqual(0);
                const table2 = await perspective.table(updated.delta!);
                const view2 = await table2.view();

                const json = await view.to_json();
                expect(json).toHaveLength(1);
                expect(json).toEqual([{ x: 5, y: 6 }]);

                const json2 = await view2.to_json();
                expect(json2).toEqual(json);

                view2.delete();
                table2.delete();
                view.delete();
                table.delete();
                emit!(undefined);
            };

            let result = new Promise((x) => {
                emit = x;
            });

            view.on_update(callback, { mode: "row" });

            let json = await view.to_json();
            expect(json).toHaveLength(2);
            expect(json).toEqual([
                { x: 1, y: 2 },
                { x: 3, y: 4 },
            ]);

            await table.replace([{ x: 5, y: 6 }]);
            await result;
        });

        test("replace the rows in the table atomically", async function () {
            const table = await perspective.table([
                { x: 1, y: 2 },
                { x: 3, y: 4 },
            ]);
            const view = await table.view();
            setTimeout(() => table.replace([{ x: 5, y: 6 }]));
            let json = await view.to_json();
            expect(json).toHaveLength(2);
            expect(json).toEqual([
                { x: 1, y: 2 },
                { x: 3, y: 4 },
            ]);

            await new Promise((x, y) => setTimeout(x));
            json = await view.to_json();
            expect(json).toHaveLength(1);
            expect(json).toEqual([{ x: 5, y: 6 }]);
            view.delete();
            table.delete();
        });

        test("Preserves sort order with 2-sided pivot", async function () {
            const input = [
                { x: 1, y: 7, z: "a" },
                { x: 1, y: 6, z: "b" },
                { x: 2, y: 5, z: "a" },
                { x: 2, y: 4, z: "b" },
                { x: 3, y: 3, z: "a" },
                { x: 3, y: 2, z: "b" },
            ];
            const table = await perspective.table(input);
            const view = await table.view({
                group_by: ["z"],
                split_by: ["x"],
                sort: [["y", "asc"]],
                columns: ["y"],
            });

            setTimeout(() => table.replace(input));
            let json = await view.to_json();
            await new Promise((x, y) => setTimeout(x));
            let json2 = await view.to_json();
            expect(json).toEqual(json2);
            view.delete();
            table.delete();
        });
    });
})(perspective);
