/* eslint-disable sonarjs/no-identical-functions */
import * as React from "react";

import { useState, useCallback, useMemo } from "@storybook/addons";
import { BuilderThemeWrapper } from "../../stories/story-utils.js";
import {
    CompactSelection,
    type EditableGridCell,
    type GridCell,
    GridCellKind,
    type GridColumn,
    type GridSelection,
    type Item,
} from "../../internal/data-grid/data-grid-types.js";
import { DataEditorAll as DataEditor } from "../../data-editor-all.js";

export default {
    title: "Tests/TestCases",

    decorators: [
        (Story: React.ComponentType) => (
            <BuilderThemeWrapper width={1000} height={800}>
                <Story />
            </BuilderThemeWrapper>
        ),
    ],
};

function getDummyData([col, row]: Item): GridCell {
    if (col === 0) {
        return {
            kind: GridCellKind.RowID,
            data: `RowID ${col}, ${row}`,
            allowOverlay: false,
        };
    }
    if (col === 1) {
        return {
            kind: GridCellKind.Bubble,
            data: [
                `Bub ${col}`,
                `Bub ${row}`,
                `Bub ${col}`,
                `Bub ${row}`,
                `Bub ${col}`,
                `Bub ${row}`,
                `Bub ${col}`,
                `Bub ${row}`,
                `Bub ${col}`,
                `Bub ${row}`,
            ],
            allowOverlay: true,
        };
    }
    if (col === 2) {
        return {
            kind: GridCellKind.Image,
            data: [
                "https://i.imgur.com/5J0BftG.jpg",
                "https://preview.redd.it/7jlqkp2cyap51.jpg?width=575&auto=webp&s=26fa9ed15b16fb450ee08ed1f2f0ccb5e0223581",
            ],
            allowOverlay: true,
            readonly: false,
        };
    }
    if (col === 3) {
        return {
            kind: GridCellKind.Markdown,
            data: `## Markdown has titles

And supports newline chars and automatic wrapping text that just needs to be long enough to trigger it.


[Google](https://google.com)

- with
- lists
- that
- can
- be
- pretty
- long
                    `,
            allowOverlay: true,
        };
    }
    if (col === 4) {
        return {
            kind: GridCellKind.Number,
            displayData: "$10,352",
            allowOverlay: true,
            data: 10_352,
            readonly: true,
        };
    }
    if (col === 5) {
        return {
            kind: GridCellKind.Uri,
            data: "https://www.google.com",
            allowOverlay: true,
        };
    }
    if (col === 6) {
        return {
            kind: GridCellKind.Boolean,
            data: row % 3 === 0 || row % 5 === 0,
            readonly: true,
            allowOverlay: false,
        };
    }
    if (col === 7) {
        return {
            kind: GridCellKind.Text,
            // RTL test
            displayData: `הרפתקה חדשה`,
            data: `הרפתקה חדשה`,
            allowOverlay: true,
            readonly: true,
        };
    }
    if (col === 8) {
        return {
            kind: GridCellKind.Drilldown,
            data: [
                {
                    text: "Test",
                    img: "https://allthatsinteresting.com/wordpress/wp-content/uploads/2012/06/iconic-photos-1950-einstein.jpg",
                },
                { text: "No Image" },
            ],
            allowOverlay: true,
        };
    }
    return {
        kind: GridCellKind.Text,
        displayData: `${col}, ${row} 🦝`,
        data: `${col}, ${row} 🦝`,
        allowOverlay: true,
    };
}

function getDummyCols() {
    return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map(
        i =>
            ({
                title: i.toString() + " is the longest header in the world",
                width: 120 + (i % 4) * 10,
                icon: "headerString",
                hasMenu: true,
            }) as GridColumn
    );
}

export function Simplenotest() {
    const [cols, setColumns] = useState(getDummyCols);

    const onColumnResize = useCallback(
        (col: GridColumn, newSize: number) => {
            const index = cols.indexOf(col);
            const newCols = [...cols];
            newCols[index] = {
                ...newCols[index],
                width: newSize,
            };
            setColumns(newCols);
        },
        [cols]
    );

    return (
        <DataEditor
            width="100%"
            getCellContent={getDummyData}
            getCellsForSelection={true}
            columns={cols}
            rows={1000}
            onColumnResize={onColumnResize}
        />
    );
}

function getDummyRelationColumn(): GridColumn[] {
    return [
        {
            title: "Relation",
            width: 360,
            icon: "headerString",
            hasMenu: true,
        },
    ];
}

function getDummyRelationData([col, row]: Item): GridCell {
    return {
        kind: GridCellKind.Drilldown,
        data: [
            {
                text: `Image ${col}-${row}`,
                img: "https://allthatsinteresting.com/wordpress/wp-content/uploads/2012/06/iconic-photos-1950-einstein.jpg",
            },
            { text: `Text ${col}-${row}` },
            { text: `More text ${col}-${row}` },
        ],
        allowOverlay: true,
    };
}

export function RelationColumn() {
    const [cols, setColumns] = useState(getDummyRelationColumn);

    const onColumnResize = useCallback(
        (col: GridColumn, newSize: number) => {
            const index = cols.indexOf(col);
            const newCols = [...cols];
            newCols[index] = {
                ...newCols[index],
                width: newSize,
            };
            setColumns(newCols);
        },
        [cols]
    );

    return (
        <DataEditor
            width="100%"
            getCellContent={getDummyRelationData}
            columns={cols}
            rows={1000}
            onColumnResize={onColumnResize}
            smoothScrollX={true}
            smoothScrollY={true}
        />
    );
}

const columns: GridColumn[] = [
    { title: "Number", width: 100, icon: "headerArray", overlayIcon: "rowOwnerOverlay" },
    { title: "Square", width: 100 },
];

function getData([col, row]: Item): GridCell {
    const n = Math.pow(row, col + 1);

    return {
        kind: GridCellKind.Number,
        data: n,
        displayData: n.toString(),
        allowOverlay: false,
    };
}

export function Minimal() {
    return <DataEditor width="100%" getCellContent={getData} columns={columns} rows={1000} />;
}

export function Smooth() {
    const [cols, setCols] = useState(getDummyCols);

    const onColumnResize = useCallback(
        (column: GridColumn, newSize: number) => {
            const index = cols.indexOf(column);
            if (index !== -1) {
                const newCol: GridColumn = {
                    ...column,
                    width: newSize,
                };

                const newCols = [...cols];
                newCols.splice(index, 1, newCol);

                setCols(newCols);
            }
        },
        [cols]
    );

    return (
        <DataEditor
            width="100%"
            getCellContent={getDummyData}
            onColumnResize={onColumnResize}
            columns={cols}
            rows={1000}
            smoothScrollY={true}
            smoothScrollX={true}
        />
    );
}

export function ManualControl() {
    const [gridSelection, setGridSelection] = useState<GridSelection | undefined>(undefined);

    const cb = (newVal: GridSelection) => {
        if ((newVal.current?.cell[0] ?? 0) % 2 === 0) {
            setGridSelection(newVal);
        }
    };

    return (
        <DataEditor
            width="100%"
            gridSelection={gridSelection}
            onGridSelectionChange={cb}
            getCellContent={getData}
            columns={columns}
            rows={1000}
        />
    );
}

export function Draggable() {
    return (
        <DataEditor
            width="100%"
            isDraggable={true}
            onDragStart={args => {
                args.setData("text", "testing");
            }}
            getCellContent={getData}
            columns={columns}
            rows={1000}
        />
    );
}

export function IdealSize() {
    // trying to be 500x500
    const cols: GridColumn[] = [
        { title: "Number", width: 250 },
        { title: "Square", width: 250 },
    ];
    return (
        <div style={{ width: 500, height: 500, position: "relative" }}>
            <DataEditor
                width={500}
                height={500}
                isDraggable={true}
                onDragStart={args => {
                    args.setData("text", "testing");
                }}
                getCellContent={getData}
                columns={cols}
                smoothScrollX={true}
                smoothScrollY={true}
                rowHeight={50}
                headerHeight={50}
                rows={9}
            />
        </div>
    );
}

export function DynamicAddRemoveColumns({ columnCount }: { columnCount: number }) {
    // trying to be 500x500
    const cols: GridColumn[] = [
        { title: "Number", width: 250 },
        { title: "Square", width: 250 },
    ];

    for (let i = 2; i < columnCount; i++) {
        cols.push({
            title: "Foo",
            width: 250,
        });
    }

    return (
        <DataEditor
            width="100%"
            isDraggable={true}
            getCellContent={getData}
            columns={cols}
            smoothScrollX={true}
            smoothScrollY={true}
            rowHeight={50}
            headerHeight={50}
            rows={9}
        />
    );
}
DynamicAddRemoveColumns.args = {
    columnCount: 2,
};

export function GridSelectionOutOfRangeNoColumns() {
    const dummyCols = useMemo(
        () => getDummyCols().map(v => ({ ...v, width: 300, title: "Making column smaller used to crash!" })),
        []
    );

    const [selected, setSelected] = useState<GridSelection | undefined>({
        current: { cell: [2, 8], range: { width: 1, height: 1, x: 2, y: 8 }, rangeStack: [] },
        columns: CompactSelection.empty(),
        rows: CompactSelection.empty(),
    });

    const [cols, setCols] = useState(dummyCols);

    const onSelected = useCallback((newSel?: GridSelection) => {
        setSelected(newSel);
    }, []);

    return (
        <DataEditor
            width="100%"
            getCellContent={getDummyData}
            columns={cols}
            rows={1000}
            onGridSelectionChange={onSelected}
            gridSelection={selected}
            onColumnResize={(_col, newSize) => {
                if (newSize > 300) {
                    setCols(dummyCols);
                } else {
                    setCols([]);
                }
            }}
        />
    );
}

type ResizableColumnsSizeMap = Record<string, number>;

function getResizableColumnsInitSize(): ResizableColumnsSizeMap {
    return {
        "resize me 0": 120,
        "resize me 1": 120,
        "resize me 2": 120,
        "resize me 3": 120,
        "resize me 4": 120,
        "resize me 5": 120,
        "resize me 6": 120,
        "resize me 7": 120,
    };
}

function getResizableColumns(sizeMap: ResizableColumnsSizeMap): GridColumn[] {
    return Object.entries(sizeMap).map(([title, width]) => ({
        title,
        width,
        icon: "headerString",
        hasMenu: true,
    }));
}

export function ResizableColumns() {
    const [colSizes, setColSizes] = useState(getResizableColumnsInitSize);

    const cols = useMemo(() => {
        return getResizableColumns(colSizes);
    }, [colSizes]);

    const onColumnResize = useCallback((column: GridColumn, newSize: number) => {
        setColSizes(prevColSizes => {
            return {
                ...prevColSizes,
                [column.title]: newSize,
            };
        });
    }, []);

    return (
        <DataEditor
            width="100%"
            getCellContent={getDummyData}
            columns={cols}
            rows={20}
            isDraggable={false}
            smoothScrollX={true}
            smoothScrollY={true}
            onColumnResize={onColumnResize}
        />
    );
}

export function GridSelectionOutOfRangeLessColumnsThanSelection() {
    const dummyCols = useMemo(
        () => getDummyCols().map(v => ({ ...v, width: 300, title: "Making column smaller used to crash!" })),
        []
    );

    const [selected, setSelected] = useState<GridSelection | undefined>({
        current: { cell: [2, 8], range: { width: 1, height: 1, x: 2, y: 8 }, rangeStack: [] },
        columns: CompactSelection.empty(),
        rows: CompactSelection.empty(),
    });

    const [cols, setCols] = useState(dummyCols);

    const onSelected = useCallback((newSel?: GridSelection) => {
        setSelected(newSel);
    }, []);

    return (
        <DataEditor
            width="100%"
            getCellContent={getDummyData}
            columns={cols}
            rows={1000}
            onGridSelectionChange={onSelected}
            gridSelection={selected}
            onColumnResize={(_col, newSize) => {
                if (newSize > 300) {
                    setCols(dummyCols);
                } else {
                    setCols([dummyCols[0]]);
                }
            }}
        />
    );
}

export function GridAddNewRows() {
    const cols = useMemo(getDummyCols, []);

    const [rowsCount, setRowsCount] = useState(10);

    const onRowAppended = useCallback(() => {
        setRowsCount(r => r + 1);
    }, []);

    const [selected, setSelected] = useState<GridSelection | undefined>(undefined);

    const onSelected = useCallback((newSel?: GridSelection) => {
        setSelected(newSel);
    }, []);

    return (
        <DataEditor
            width="100%"
            getCellContent={getDummyData}
            columns={cols}
            rows={rowsCount}
            onRowAppended={onRowAppended}
            onGridSelectionChange={onSelected}
            gridSelection={selected}
        />
    );
}

export function GridNoTrailingBlankRow() {
    const cols = useMemo(getDummyCols, []);

    const [selected, setSelected] = useState<GridSelection | undefined>(undefined);

    const onSelected = useCallback((newSel?: GridSelection) => {
        setSelected(newSel);
    }, []);

    return (
        <DataEditor
            width="100%"
            getCellContent={getDummyData}
            columns={cols}
            rows={100}
            onGridSelectionChange={onSelected}
            gridSelection={selected}
        />
    );
}

export function MarkdownEdits() {
    const dummyCols: GridColumn[] = useMemo(() => {
        return [
            {
                title: "MD short",
                width: 50,
            },
            {
                title: "MD long",
                width: 50,
            },
        ];
    }, []);

    const dummyCells = useCallback(([col, _row]: Item) => {
        if (col === 0) {
            const editable: EditableGridCell = {
                data: "text",
                allowOverlay: true,
                kind: GridCellKind.Markdown,
            };
            return editable;
        } else if (col === 1) {
            const editable: EditableGridCell = {
                data: `text really really really long
## H1

- this
- is
- a
- longer
- example
- to
- test
- scroll
- of
- preview
                `,
                allowOverlay: true,
                kind: GridCellKind.Markdown,
            };
            return editable;
        }
        const editable: EditableGridCell = {
            data: "text",
            allowOverlay: true,
            kind: GridCellKind.Markdown,
        };
        return editable;
    }, []);

    const [selected, setSelected] = useState<GridSelection | undefined>({
        current: { cell: [2, 8], range: { width: 1, height: 1, x: 2, y: 8 }, rangeStack: [] },
        columns: CompactSelection.empty(),
        rows: CompactSelection.empty(),
    });

    const onSelected = useCallback((newSel?: GridSelection) => {
        setSelected(newSel);
    }, []);

    return (
        <DataEditor
            width="100%"
            getCellContent={dummyCells}
            columns={dummyCols}
            rows={1000}
            onGridSelectionChange={onSelected}
            gridSelection={selected}
        />
    );
}

export const CanEditBoolean = () => {
    const [vals, setVals] = useState<[boolean | null | undefined, boolean | null | undefined]>([false, false]);
    return (
        <DataEditor
            width="100%"
            columns={[
                {
                    title: "Editable",
                    width: 100,
                },
                {
                    title: "Readonly",
                    width: 100,
                },
            ]}
            rows={1}
            getCellContent={([col]) => {
                return {
                    kind: GridCellKind.Boolean,
                    readonly: col !== 0,
                    allowOverlay: false,
                    data: vals[col],
                };
            }}
            onCellEdited={([col], newVal) => {
                if (newVal.kind === GridCellKind.Boolean) {
                    setVals(cv => {
                        const f = [...cv];
                        f.splice(col, 1, newVal.data);
                        return f as [boolean, boolean];
                    });
                }
            }}
        />
    );
};

export const SimpleEditable = () => {
    const [vals, setVals] = useState<[string, string][]>(() => {
        const result: [string, string][] = [];
        for (let i = 0; i < 2000; i++) {
            result.push(["Edit", "Me"]);
        }
        return result;
    });

    return (
        <DataEditor
            width="100%"
            columns={[
                {
                    title: "Column A",
                    width: 250,
                },
                {
                    title: "Column B",
                    width: 250,
                },
            ]}
            rows={vals.length}
            getCellContent={([col, row]) => ({
                kind: GridCellKind.Text,
                allowOverlay: true,
                data: vals[row][col],
                displayData: vals[row][col],
            })}
            onCellEdited={([col, row], newVal) => {
                const newVals = [...vals];
                const newRow: [string, string] = [...newVals[row]];
                if (typeof newVal.data === "string") {
                    newRow[col] = newVal.data;
                }
                newVals[row] = newRow;
                setVals(newVals);
            }}
        />
    );
};
