import { animationFrame } from "@odoo/hoot-mock";
import { describe, expect, test, beforeEach } from "@odoo/hoot";
import {
    defineSpreadsheetActions,
    defineSpreadsheetModels,
} from "@spreadsheet/../tests/helpers/data";

import { setCellContent, updatePivot } from "@spreadsheet/../tests/helpers/commands";
import {
    getEvaluatedCell,
    getEvaluatedFormatGrid,
    getEvaluatedGrid,
} from "@spreadsheet/../tests/helpers/getters";
import { createSpreadsheetWithPivot } from "@spreadsheet/../tests/helpers/pivot";

let model;

describe.current.tags("headless");
defineSpreadsheetModels();
defineSpreadsheetActions();

beforeEach(async () => {
    ({ model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
            <pivot>
                <field name="product_id" type="col"/>
                <field name="foo" type="row"/>
                <field name="probability" type="measure"/>
            </pivot>`,
    }));
    model.dispatch("CREATE_SHEET", { sheetId: "42" });
});

test("full PIVOT() values", async function () {
    setCellContent(model, "A1", `=PIVOT("1")`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D7", "42")).toEqual([
        ["Partner Pivot",         "xphone",       "xpad",         "Total"],
        ["",            "Probability",  "Probability",  "Probability"],
        [1,             "",             11,             11],
        [2,             "",             15,             15],
        [12,            10,             "",             10],
        [17,            "",             95,             95],
        ["Total",       10,             121,            131],
    ]);
});

test("full PIVOT() formats", async function () {
    setCellContent(model, "A1", `=PIVOT("1")`, "42");
    // prettier-ignore
    expect(getEvaluatedFormatGrid(model, "A1:D7", "42")).toEqual([
        [undefined, "@* ",      "@* ",      undefined],
        [undefined, undefined,  undefined,  undefined],
        ["0* ",     "#,##0.00", "#,##0.00", "#,##0.00"],
        ["0* ",     "#,##0.00", "#,##0.00", "#,##0.00"],
        ["0* ",     "#,##0.00", "#,##0.00", "#,##0.00"],
        ["0* ",     "#,##0.00", "#,##0.00", "#,##0.00"],
        [undefined, "#,##0.00", "#,##0.00", "#,##0.00"],
    ]);
});

test("PIVOT(row_count=1)", async function () {
    setCellContent(model, "A1", `=PIVOT("1", 1)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D4", "42")).toEqual([
        ["Partner Pivot",       "xphone",       "xpad",         "Total"],
        ["",                    "Probability",  "Probability",  "Probability"],
        [1,                     "",             11,             11],
        [null,                  null,           null,           null],
    ]);
});

test("PIVOT(row_count=0)", async function () {
    setCellContent(model, "A1", `=PIVOT("1", 0)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D3", "42")).toEqual([
        ["Partner Pivot",       "xphone",       "xpad",         "Total"],
        ["",                    "Probability",  "Probability",  "Probability"],
        [null,                  null,           null,           null],
    ]);
});

test("PIVOT(negative row_count)", async function () {
    setCellContent(model, "A1", `=PIVOT("1", -1)`, "42");
    expect(getEvaluatedCell(model, "A1", "42").value).toBe("#ERROR");
    expect(getEvaluatedCell(model, "A1", "42").message).toBe(
        "The number of rows must be positive."
    );
});

test("PIVOT(include_column_titles=FALSE)", async function () {
    setCellContent(model, "A1", `=PIVOT("1",,,FALSE,,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D5", "42")).toEqual([
        [1,         "",             11,             11],
        [2,         "",             15,             15],
        [12,        10,             "",             10],
        [17,        "",             95,             95],
        ["Total",   10,             121,            131],
    ]);
});

test("PIVOT(include_total=FALSE) with no groupbys applied", async function () {
    const { model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
        <pivot>
            <field name="probability" type="measure"/>
        </pivot>`,
    });
    model.dispatch("CREATE_SHEET", { sheetId: "42" });
    setCellContent(model, "A1", `=PIVOT("1",,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:B3", "42")).toEqual([
            ["Partner Pivot",       "Total"],
            ["",                    "Probability"],
            ["Total",               131],
        ]);
});

test("PIVOT(include_total=FALSE) with multiple measures and no groupbys applied", async function () {
    const { model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
        <pivot>
            <field name="probability" type="measure"/>
            <field name="foo" type="measure"/>
        </pivot>`,
    });
    model.dispatch("CREATE_SHEET", { sheetId: "42" });
    setCellContent(model, "A1", `=PIVOT("1",,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:C3", "42")).toEqual([
            ["Partner Pivot",       "Total",        ""],
            ["",                    "Probability",  "Foo"],
            ["Total",               131,            32],
        ]);
});

test("PIVOT(include_total=FALSE) with only row groupby applied", async function () {
    const { model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
        <pivot>
                <field name="foo" type="row"/>
                <field name="probability" type="measure"/>
            </pivot>`,
    });
    model.dispatch("CREATE_SHEET", { sheetId: "42" });
    setCellContent(model, "A1", `=PIVOT("1",,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:C7", "42")).toEqual([
            ["Partner Pivot",       "Total",        null],
            ["",                    "Probability",  null],
            [1,                     11,             null],
            [2,                     15,             null],
            [12,                    10,             null],
            [17,                    95,             null],
            [null,                  null,           null],
        ]);
});

test("sorted PIVOT(include_total=FALSE) with only row groupby applied", async function () {
    const { model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
        <pivot>
            <field name="foo" type="row"/>
            <field name="probability" type="measure"/>
        </pivot>`,
    });
    const [pivotId] = model.getters.getPivotIds();
    updatePivot(model, pivotId, {
        sortedColumn: {
            domain: [],
            order: "desc",
            measure: "probability:avg",
        },
    });
    model.dispatch("CREATE_SHEET", { sheetId: "42" });
    setCellContent(model, "A1", `=PIVOT("1",,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:C7", "42")).toEqual([
        ["Partner Pivot",       "Total",        null],
        ["",                    "Probability",  null],
        [17,                    95,             null],
        [2,                     15,             null],
        [1,                     11,             null],
        [12,                    10,             null],
        [null,                  null,           null],
    ]);
});

test("PIVOT(include_total=FALSE) with multiple measures and only row groupby applied", async function () {
    const { model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
        <pivot>
                <field name="product_id" type="row"/>
                <field name="probability" type="measure"/>
                <field name="foo" type="measure"/>
            </pivot>`,
    });
    model.dispatch("CREATE_SHEET", { sheetId: "42" });
    setCellContent(model, "A1", `=PIVOT("1",,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D5", "42")).toEqual([
            ["Partner Pivot",           "Total",            "",        null],
            ["",                        "Probability",      "Foo",     null],
            ["xphone",                  10,                 12,        null],
            ["xpad",                    121,                20,        null],
            [null,                      null,               null,      null],
        ]);
});

test("PIVOT(include_total=FALSE) with only col groupby applied", async function () {
    const { model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
        <pivot>
            <field name="product_id" type="col"/>
            <field name="probability" type="measure"/>
        </pivot>`,
    });
    model.dispatch("CREATE_SHEET", { sheetId: "42" });
    setCellContent(model, "A1", `=PIVOT("1",,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D4", "42")).toEqual([
            ["Partner Pivot",          "xphone",          "xpad",            null],
            ["",                       "Probability",     "Probability",     null],
            ["Total",                  10,                121,               null],
            [null,                     null,              null,              null],
        ]);
});

test("PIVOT(include_total=FALSE, include_column_titles=FALSE)", async function () {
    setCellContent(model, "A1", `=PIVOT("1",,FALSE,FALSE,,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D5", "42")).toEqual([
            [1,         "",             11,             null],
            [2,         "",             15,             null],
            [12,        10,             "",             null],
            [17,        "",             95,             null],
            [null,      null,           null,           null],
        ]);
});

test("PIVOT(row_count=1, include_total=FALSE, include_column_titles=FALSE)", async function () {
    setCellContent(model, "A1", `=PIVOT("1",1,FALSE,FALSE,,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D2", "42")).toEqual([
            [1,         "",             11,             null],
            [null,      null,           null,           null],
        ]);
});

test("PIVOT(row_count=0, include_total=FALSE, include_column_titles=FALSE)", async function () {
    setCellContent(model, "A1", `=PIVOT("1",0,FALSE,FALSE,,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D1", "42")).toEqual([
            ["Partner Pivot",      null, null, null],
        ]);
});

test("PIVOT(row_count=0, include_total=TRUE, include_column_titles=FALSE)", async function () {
    setCellContent(model, "A1", `=PIVOT("1",0,TRUE,FALSE,,FALSE)`, "42");
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D1", "42")).toEqual([
            ["Partner Pivot",      null, null, null],
        ]);
});

test("PIVOT with multiple row groups", async function () {
    const { model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
            <pivot>
                <field name="product_id" type="col"/>
                <field name="foo" type="row"/>
                <field name="id" type="row"/>
                <field name="probability" type="measure"/>
            </pivot>`,
    });
    const firstSheetId = model.getters.getActiveSheetId();
    // values in the first sheet from the individual pivot functions
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D11", firstSheetId)).toEqual([
        ["Partner Pivot",      "xphone",       "xpad",         "Total"],
        ["",                   "Probability",  "Probability",  "Probability"],
        [1,                     "",             11,             11],
        ["Steven",              "",             11,             11],
        [2,                     "",             15,             15],
        ["Zara",                "",             15,             15],
        [12,                    10,             "",             10],
        ["Raoul",               10,             "",             10],
        [17,                    "",             95,             95],
        ["Taylor",              "",             95,             95],
        ["Total",               10,             121,            131],
    ]);
    model.dispatch("CREATE_SHEET", { sheetId: "42" });
    setCellContent(model, "A1", `=PIVOT("1")`, "42");
    // values from the PIVOT function
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D11", "42")).toEqual([
        ["Partner Pivot",       "xphone",       "xpad",         "Total"],
        ["",                    "Probability",  "Probability",  "Probability"],
        [1,                     "",             11,             11],
        ["Steven",              "",             11,             11],
        [2,                     "",             15,             15],
        ["Zara",                "",             15,             15],
        [12,                    10,             "",             10],
        ["Raoul",               10,             "",             10],
        [17,                    "",             95,             95],
        ["Taylor",              "",             95,             95],
        ["Total",               10,             121,            131],
    ]);
    setCellContent(model, "A1", `=PIVOT("1",,FALSE)`, "42");
    // values from the PIVOT function without any group totals
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:D11", "42")).toEqual([
        ["Partner Pivot",      "xphone",       "xpad",         null],
        ["",                   "Probability",  "Probability",  null],
        [1,                    "",             "",             null], // group header but without total values
        ["Steven",             "",             11,             null],
        [2,                    "",             "",             null], // group header but without total values
        ["Zara",               "",             15,             null],
        [12,                   "",             "",             null], // group header but without total values
        ["Raoul",              10,             "",             null],
        [17,                   "",             "",             null], // group header but without total values
        ["Taylor",             "",             95,             null],
        [null,                 null,           null,           null],
    ]);
});

test("edit pivot groups", async function () {
    setCellContent(model, "A1", `=PIVOT("1")`, "42");
    const originalGrid = getEvaluatedGrid(model, "A1:D7", "42");
    // prettier-ignore
    expect(originalGrid).toEqual([
        ["Partner Pivot",         "xphone",       "xpad",         "Total"],
        ["",            "Probability",  "Probability",  "Probability"],
        [1,             "",             11,             11],
        [2,             "",             15,             15],
        [12,            10,             "",             10],
        [17,            "",             95,             95],
        ["Total",       10,             121,            131],
    ]);
    const [pivotId] = model.getters.getPivotIds();
    model.dispatch("UPDATE_PIVOT", {
        pivotId,
        pivot: {
            ...model.getters.getPivotCoreDefinition(pivotId),
            columns: [],
            rows: [],
        },
    });
    await animationFrame();
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:B3", "42")).toEqual([
        ["Partner Pivot",       "Total"],
        ["",                    "Probability"],
        ["Total",               131],
    ]);
    model.dispatch("REQUEST_UNDO");
    await animationFrame();
    expect(getEvaluatedGrid(model, "A1:D7", "42")).toEqual(originalGrid);
});

test("Renaming the pivot reevaluates the PIVOT function", async function () {
    const pivotId = model.getters.getPivotIds()[0];
    setCellContent(model, "A1", `=PIVOT("1")`, "42");
    expect(getEvaluatedCell(model, "A1", "42").value).toBe("Partner Pivot");
    model.dispatch("RENAME_PIVOT", {
        pivotId,
        name: "New Name",
    });
    expect(getEvaluatedCell(model, "A1", "42").value).toBe("New Name");
});

test("can hide a measure", async function () {
    const { model } = await createSpreadsheetWithPivot({
        arch: /* xml */ `
        <pivot>
            <field name="probability" type="measure"/>
            <field name="foo" type="measure"/>
        </pivot>`,
    });
    setCellContent(model, "A10", '=PIVOT("1")');
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A10:C12")).toEqual([
        ["Partner Pivot",           "Total",            "",],
        ["",                        "Probability",      "Foo"],
        ["Total",                   131,                32],
    ]);
    const [pivotId] = model.getters.getPivotIds();
    const definition = model.getters.getPivotCoreDefinition(pivotId);
    updatePivot(model, pivotId, {
        measures: [{ ...definition.measures[0], isHidden: true }, definition.measures[1]],
    });
    await animationFrame();
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A10:C12")).toEqual([
        ["Partner Pivot",           "Total",    null],
        ["",                        "Foo",      null],
        ["Total",                   32,         null],
    ]);
});

test("can have a dimension with a field of a relational field", async function () {
    const { model, pivotId } = await createSpreadsheetWithPivot();
    updatePivot(model, pivotId, {
        rows: [{ fieldName: "product_id.display_name", order: "asc" }],
        columns: [],
    });
    await animationFrame();
    // prettier-ignore
    expect(getEvaluatedGrid(model, "A1:B5")).toEqual([
        ["Partner Pivot", "Total"],
        ["",              "Probability"],
        ["xpad",          121],
        ["xphone",        10],
        ["Total",         131],
    ]);
});
