/** @odoo-module */

import { ormService } from "@web/core/orm_service";
import { registry } from "@web/core/registry";
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
import {
    DashboardLoader,
    Status,
} from "@spreadsheet_dashboard/bundle/dashboard_action/dashboard_loader";
import { nextTick, patchWithCleanup } from "@web/../tests/helpers/utils";
import { getDashboardServerData } from "../utils/data";

import { waitForDataSourcesLoaded } from "@spreadsheet/../tests/utils/model";
import { getCellValue } from "@spreadsheet/../tests/utils/getters";
import { RPCError } from "@web/core/network/rpc_service";

/**
 * @param {object} [params]
 * @param {object} [params.serverData]
 * @param {function} [params.mockRPC]
 * @returns {Promise<DashboardLoader>}
 */
async function createDashboardLoader(params = {}) {
    registry.category("services").add("orm", ormService);
    const env = await makeTestEnv({
        serverData: params.serverData || getDashboardServerData(),
        mockRPC: params.mockRPC,
    });
    return new DashboardLoader(env, env.services.orm, async (dashboardId) => {
        const [record] = await env.services.orm.read(
            "spreadsheet.dashboard",
            [dashboardId],
            ["raw"]
        );
        return { data: record.raw, revisions: [] };
    });
}

QUnit.module("spreadsheet_dashboard > Dashboard loader");

QUnit.test("load all dashboards of all containers", async (assert) => {
    const loader = await createDashboardLoader();
    loader.load();
    assert.deepEqual(loader.getDashboardGroups(), []);
    await nextTick();
    assert.deepEqual(loader.getDashboardGroups(), [
        {
            id: 1,
            name: "Container 1",
            dashboards: [
                {
                    id: 1,
                    displayName: "Dashboard CRM 1",
                    status: Status.NotLoaded,
                },
                {
                    id: 2,
                    displayName: "Dashboard CRM 2",
                    status: Status.NotLoaded,
                },
            ],
        },
        {
            id: 2,
            name: "Container 2",
            dashboards: [
                {
                    id: 3,
                    displayName: "Dashboard Accounting 1",
                    status: Status.NotLoaded,
                },
            ],
        },
    ]);
});

QUnit.test("load twice does not duplicate spreadsheets", async (assert) => {
    const loader = await createDashboardLoader();
    await loader.load();
    assert.deepEqual(loader.getDashboardGroups()[1].dashboards, [
        { id: 3, displayName: "Dashboard Accounting 1", status: Status.NotLoaded },
    ]);
    await loader.load();
    assert.deepEqual(loader.getDashboardGroups()[1].dashboards, [
        { id: 3, displayName: "Dashboard Accounting 1", status: Status.NotLoaded },
    ]);
});

QUnit.test("load spreadsheet data", async (assert) => {
    const loader = await createDashboardLoader();
    await loader.load();
    const result = loader.getDashboard(3);
    assert.strictEqual(result.status, Status.Loading);
    await nextTick();
    assert.strictEqual(result.status, Status.Loaded);
    assert.ok(result.model);
});

QUnit.test("load spreadsheet data only once", async (assert) => {
    const loader = await createDashboardLoader({
        mockRPC: function (route, args) {
            if (args.method === "read") {
                assert.step(`spreadsheet ${args.args[0]} loaded`);
            }
        },
    });
    await loader.load();
    let result = loader.getDashboard(3);
    await nextTick();
    assert.strictEqual(result.status, Status.Loaded);
    assert.verifySteps(["spreadsheet 1,2,3 loaded", "spreadsheet 3 loaded"]);
    result = loader.getDashboard(3);
    await nextTick();
    assert.strictEqual(result.status, Status.Loaded);
    assert.verifySteps([]);
});

QUnit.test("don't return empty dashboard group", async (assert) => {
    const loader = await createDashboardLoader({
        mockRPC: async function (route, args) {
            if (args.method === "search_read" && args.model === "spreadsheet.dashboard.group") {
                return [
                    {
                        id: 45,
                        name: "Group A",
                        dashboard_ids: [1],
                    },
                    {
                        id: 46,
                        name: "Group B",
                        dashboard_ids: [],
                    },
                ];
            }
        },
    });
    await loader.load();
    assert.deepEqual(loader.getDashboardGroups(), [
        {
            id: 45,
            name: "Group A",
            dashboards: [
                {
                    id: 1,
                    displayName: "Dashboard CRM 1",
                    status: Status.NotLoaded,
                },
            ],
        },
    ]);
});

QUnit.test("load multiple spreadsheets", async (assert) => {
    const loader = await createDashboardLoader({
        mockRPC: function (route, args) {
            if (args.method === "read") {
                assert.step(`spreadsheet ${args.args[0]} loaded`);
            }
        },
    });
    await loader.load();
    assert.verifySteps(["spreadsheet 1,2,3 loaded"]);
    loader.getDashboard(1);
    await nextTick();
    assert.verifySteps(["spreadsheet 1 loaded"]);
    loader.getDashboard(2);
    await nextTick();
    assert.verifySteps(["spreadsheet 2 loaded"]);
    loader.getDashboard(1);
    await nextTick();
    assert.verifySteps([]);
});

QUnit.test("load spreadsheet data with error", async (assert) => {
    const loader = await createDashboardLoader({
        mockRPC: function (route, args) {
            if (
                args.method === "read" &&
                args.model === "spreadsheet.dashboard" &&
                args.args[1][0] === "raw"
            ) {
                const error = new RPCError();
                error.data = { message: "Bip" };
                throw error;
            }
        },
    });
    await loader.load();
    const result = loader.getDashboard(3);
    assert.strictEqual(result.status, Status.Loading);
    await result.promise.catch(() => assert.step("error"));
    assert.strictEqual(result.status, Status.Error);
    assert.strictEqual(result.error.data.message, "Bip");
    assert.verifySteps(["error"], "error is thrown");
});

QUnit.test("async formulas are correctly evaluated", async (assert) => {
    const spreadsheetData = {
        sheets: [
            {
                id: "sheet1",
                cells: {
                    A1: { content: `=ODOO.CURRENCY.RATE("EUR","USD")` }, // an async formula
                },
            },
        ],
    };
    const serverData = getDashboardServerData();
    const dashboardId = 15;
    serverData.models["spreadsheet.dashboard"].records = [
        {
            id: dashboardId,
            raw: JSON.stringify(spreadsheetData),
            json_data: JSON.stringify(spreadsheetData),
            name: "Dashboard Accounting 1",
            dashboard_group_id: 2,
        },
    ];
    serverData.models["spreadsheet.dashboard.group"].records = [
        { id: 1, name: "Container 1", dashboard_ids: [dashboardId] },
    ];
    const loader = await createDashboardLoader({
        serverData,
        mockRPC: function (route, args) {
            if (args.method === "get_rates_for_spreadsheet") {
                const info = args.args[0][0];
                return [{ ...info, rate: 0.9 }];
            }
        },
    });
    await loader.load();
    loader.getDashboard(dashboardId);
    await nextTick();
    const { model } = loader.getDashboard(dashboardId);
    await waitForDataSourcesLoaded(model);
    assert.strictEqual(await getCellValue(model, "A1"), 0.9);
});

QUnit.test("Model is in dashboard mode", async (assert) => {
    const loader = await createDashboardLoader();
    await loader.load();
    loader.getDashboard(3);
    await nextTick();
    const { model } = loader.getDashboard(3);
    assert.strictEqual(model.config.mode, "dashboard");
});

QUnit.test("Model is in dashboard mode", async (assert) => {
    patchWithCleanup(DashboardLoader.prototype, {
        _activateFirstSheet: () => {
            assert.step("activate sheet");
        },
    });
    const loader = await createDashboardLoader();
    await loader.load();
    loader.getDashboard(3);
    await nextTick();
    assert.verifySteps(["activate sheet"]);
});
