/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { TLBoundsCorner, Utils } from '@tldraw/core';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawCommand } from '@toeverything/components/board-types';
import { StretchType, TDShapeType } from '@toeverything/components/board-types';

export function stretchShapes(
    app: TldrawApp,
    ids: string[],
    type: StretchType
): TldrawCommand {
    const { currentPageId, selectedIds } = app;

    const initialShapes = ids.map(id => app.getShape(id));

    const boundsForShapes = initialShapes.map(shape => TLDR.get_bounds(shape));

    const commonBounds = Utils.getCommonBounds(boundsForShapes);

    const idsToMutate = ids
        .flatMap(id => {
            const shape = app.getShape(id);
            return shape.children ? shape.children : shape.id;
        })
        .filter(id => !app.getShape(id).isLocked);

    const { before, after } = TLDR.mutate_shapes(
        app.state,
        idsToMutate,
        shape => {
            const bounds = TLDR.get_bounds(shape);

            switch (type) {
                case StretchType.Horizontal: {
                    const newBounds = {
                        ...bounds,
                        minX: commonBounds.minX,
                        maxX: commonBounds.maxX,
                        width: commonBounds.width,
                    };

                    return TLDR.get_shape_util(shape).transformSingle(
                        shape,
                        newBounds,
                        {
                            type: TLBoundsCorner.TopLeft,
                            scaleX: newBounds.width / bounds.width,
                            scaleY: 1,
                            initialShape: shape,
                            transformOrigin: [0.5, 0.5],
                        }
                    );
                }
                case StretchType.Vertical: {
                    const newBounds = {
                        ...bounds,
                        minY: commonBounds.minY,
                        maxY: commonBounds.maxY,
                        height: commonBounds.height,
                    };

                    return TLDR.get_shape_util(shape).transformSingle(
                        shape,
                        newBounds,
                        {
                            type: TLBoundsCorner.TopLeft,
                            scaleX: 1,
                            scaleY: newBounds.height / bounds.height,
                            initialShape: shape,
                            transformOrigin: [0.5, 0.5],
                        }
                    );
                }
            }
        },
        currentPageId
    );

    initialShapes.forEach(shape => {
        if (shape.type === TDShapeType.Group) {
            delete before[shape.id];
            delete after[shape.id];
        }
    });

    return {
        id: 'stretch',
        before: {
            document: {
                pages: {
                    [currentPageId]: { shapes: before },
                },
                pageStates: {
                    [currentPageId]: {
                        selectedIds,
                    },
                },
            },
        },
        after: {
            document: {
                pages: {
                    [currentPageId]: { shapes: after },
                },
                pageStates: {
                    [currentPageId]: {
                        selectedIds: ids,
                    },
                },
            },
        },
    };
}
