import * as React from 'react'
import type { StoryFn, Meta } from '@storybook/react-webpack5'
import type { Column, GridRowMeta } from '..'
import { Grid, GridCellDefault, useGridRow } from '..'
import { ButtonEmpty, ButtonGroup } from '@planview/pv-uikit'
import { theme, spacing } from '@planview/pv-utilities'
import { useGridRowMeta } from '../context/grid-context'
import {
    Activity,
    ActivityParent,
    FilePowerpoint,
    FileWord,
    Folder,
    Milestone,
} from '@planview/pv-icons'

export default {
    title: 'pv-grid/Examples/Drag and Drop',
    tags: ['hidden'],
} satisfies Meta<any>

export const FlatGrid: StoryFn<any> = () => {
    type Fruit = {
        id: string
        name: string
        count: number
    }

    const [multiple, setMultiple] = React.useState(false)

    const rows = React.useMemo<Record<string, Fruit>>(
        () => ({
            1: {
                id: '1',
                name: 'Apples',
                count: 250,
            },
            2: {
                id: '2',
                name: 'Oranges',
                count: 75,
            },
            3: {
                id: '3',
                name: 'Grapes',
                count: 87,
            },
            4: {
                id: '4',
                name: 'Watermelons',
                count: 5,
            },
        }),
        []
    )

    const [ids, setIds] = React.useState(() => Object.keys(rows))

    const columns = React.useMemo<Column<Fruit>[]>(
        () => [
            {
                id: 'name',
                label: 'Name',
                width: 400,
                sortable: false,
                movable: false,
            },
            {
                id: 'count',
                label: 'Count',
                width: 200,
                sortable: false,
                movable: false,
                header: {
                    align: 'right',
                },
                cell: {
                    Renderer: ({ label, tabIndex }) => (
                        <GridCellDefault
                            numeric
                            tabIndex={tabIndex}
                            label={label}
                        />
                    ),
                },
            },
        ],
        []
    )

    return (
        <div
            style={{
                display: 'flex',
                flexDirection: 'column',
                gap: spacing.medium,
            }}
        >
            <div>
                <ButtonGroup>
                    <ButtonEmpty
                        activated={!multiple}
                        onClick={() => setMultiple(false)}
                    >
                        Single
                    </ButtonEmpty>
                    <ButtonEmpty
                        activated={multiple}
                        onClick={() => setMultiple(true)}
                    >
                        Multiple
                    </ButtonEmpty>
                </ButtonGroup>
            </div>
            <Grid
                columns={columns}
                rows={{ ids, data: rows }}
                selectionMode={multiple ? 'multi' : 'none'}
                rowDrag={{
                    multiple,
                    onDrop({ resultIds }) {
                        if (resultIds != null) {
                            setIds(resultIds)
                        }
                    },
                }}
            />
        </div>
    )
}

export const TreeGridOrdering: StoryFn<any> = () => {
    type PlanItem = {
        id: number
        name: string
        type: 'plan' | 'milestone'
        parentId?: number | null
        index: number
        risk?: string
    }

    const [planData, setPlanData] = React.useState<Array<PlanItem>>([
        { id: 1, index: 0, name: 'Build a spaceship', type: 'plan' },
        {
            id: 11,
            index: 0,
            name: 'Purchase materials',
            type: 'plan',
            parentId: 1,
            risk: 'Low',
        },
        {
            id: 12,
            index: 1,
            name: 'Produce parts',
            type: 'plan',
            parentId: 1,
            risk: 'Low',
        },
        {
            id: 13,
            index: 2,
            name: 'Assemble parts',
            type: 'plan',
            parentId: 1,
            risk: 'Medium',
        },
        {
            id: 14,
            index: 3,
            name: 'Spaceship finished',
            type: 'milestone',
            parentId: 1,
        },
        {
            id: 2,
            index: 1,
            name: 'Send spaceship to Mars',
            type: 'plan',
            risk: 'High',
        },
        {
            id: 21,
            index: 0,
            name: 'Schedule launch with mission control',
            type: 'milestone',
            parentId: 2,
        },
        {
            id: 22,
            index: 1,
            name: 'Launch spaceship',
            type: 'milestone',
            parentId: 2,
        },
        {
            id: 3,
            index: 2,
            name: 'Achieve interstellar fame',
            type: 'plan',
            risk: 'Low',
        },
    ])

    const rows = React.useMemo(() => {
        const ids: number[] = []
        const data = new Map<number, PlanItem>()
        const meta = new Map<number, GridRowMeta<PlanItem>>()

        const sortedData = planData.concat()
        sortedData.sort((a, b) => a.index - b.index)

        sortedData.forEach((p) => {
            data.set(p.id, p)
            if (p.parentId != null) {
                const children = meta.get(p.parentId)?.children ?? []
                children.push(p.id)
                meta.set(p.parentId, { type: 'tree', children })
            } else {
                ids.push(p.id)
            }
        })

        return { ids, data, meta }
    }, [planData])

    const columns = React.useMemo<Column<PlanItem>[]>(
        () => [
            {
                id: 'name',
                label: 'Name',
                width: 400,
                tree: true,
                movable: false,
                lockedLocation: 'left',
                sortable: false,
                cell: {
                    Renderer({ rowId, ...props }) {
                        const row = useGridRow<PlanItem>(rowId)
                        const meta = useGridRowMeta(rowId)

                        const Icon =
                            meta.type === 'tree'
                                ? ActivityParent
                                : row.type === 'milestone'
                                  ? Milestone
                                  : Activity

                        return (
                            <GridCellDefault
                                icon={<Icon color={theme.green500} />}
                                {...props}
                            />
                        )
                    },
                },
            },
            {
                id: 'type',
                label: 'Type',
                sortable: false,
                cell: {
                    value({ row }) {
                        return row.type === 'milestone' ? 'Milestone' : 'Plan'
                    },
                },
            },
            {
                id: 'risk',
                label: 'Risk',
                sortable: false,
            },
        ],
        []
    )

    return (
        <Grid
            columns={columns}
            rows={rows}
            selectionMode="none"
            rowDrag={{
                enableLeafConversion: true,
                canDrop({ targetParent }) {
                    if (targetParent?.row.type === 'milestone') {
                        return {
                            allowed: false,
                            message:
                                'You cannot add sub-activities to a milestone',
                        }
                    }
                    return { allowed: true }
                },
                onDrop({
                    draggedRowIds,
                    targetParent,
                    targetParentId,
                    resultIds,
                }) {
                    setPlanData((plans) => {
                        // This example is purely for the demo
                        // A more robust application of this would be needed
                        // in a production environment (or it would be
                        // handled by an API endpoint)
                        if (resultIds) {
                            // The item was dropped at a specific location
                            // Update all the siblings with the correct index
                            return plans.map((p) => {
                                if (resultIds.includes(p.id)) {
                                    return {
                                        ...p,
                                        index: resultIds.indexOf(p.id),
                                        parentId: targetParentId,
                                    }
                                }
                                return p
                            })
                        } else {
                            // The item was dropped on a parent
                            // Choose the last index
                            const children = targetParent
                                ? (targetParent.rowMeta.children ?? [])
                                : rows.ids
                            const maxIndex = children.filter(
                                (c) => !draggedRowIds.includes(c)
                            ).length
                            return plans.map((p) => {
                                if (draggedRowIds.includes(p.id)) {
                                    return {
                                        ...p,
                                        index: maxIndex,
                                        parentId: targetParentId,
                                    }
                                }
                                return p
                            })
                        }
                    })
                },
            }}
        />
    )
}

export const TreeGridParent: StoryFn<any> = () => {
    type FileItem = {
        id: number
        name: string
        type: 'slides' | 'folder' | 'document'
        parentId: number | null
    }

    const [documentData, setDocumentData] = React.useState<Array<FileItem>>([
        { id: 1, name: 'Presentations', type: 'folder', parentId: null },
        { id: 11, name: 'Introduction to Grid', type: 'slides', parentId: 1 },
        { id: 12, name: 'Advanced Grid Usage', type: 'slides', parentId: 1 },
        { id: 2, name: 'Documents', type: 'folder', parentId: null },
        { id: 21, name: 'Maintaining the Grid', type: 'document', parentId: 2 },
        { id: 22, name: 'Glossary', type: 'document', parentId: 2 },
        { id: 23, name: 'Meeting notes', type: 'document', parentId: 2 },
        { id: 24, name: 'Archive', type: 'folder', parentId: 2 },
        { id: 231, name: '2021 Notes', type: 'document', parentId: 24 },
        { id: 3, name: 'Getting started', type: 'slides', parentId: null },
        {
            id: 4,
            name: 'Read Me',
            type: 'document',
            parentId: null,
        },
    ])

    const rows = React.useMemo(() => {
        const ids: number[] = []
        const data = new Map<number, FileItem>()
        const meta = new Map<number, GridRowMeta<FileItem>>()

        documentData.forEach((d) => {
            data.set(d.id, d)
            if (d.parentId != null) {
                const children = meta.get(d.parentId)?.children ?? []
                children.push(d.id)
                meta.set(d.parentId, { type: 'tree', children })
            } else {
                ids.push(d.id)
            }

            if (d.type === 'folder') {
                meta.set(d.id, { type: 'tree', ...meta.get(d.id) })
            }
        })

        return { ids, data, meta }
    }, [documentData])

    const columns = React.useMemo<Column<FileItem>[]>(
        () => [
            {
                id: 'name',
                label: 'Name',
                width: 400,
                tree: true,
                movable: false,
                lockedLocation: 'left',
                cell: {
                    value({ row }) {
                        return row.type !== 'folder' ? `_${row.name}` : row.name
                    },
                    label({ row }) {
                        return row.name
                    },
                    Renderer({ rowId, ...props }) {
                        const row = useGridRow<FileItem>(rowId)

                        const icon =
                            row.type === 'folder' ? (
                                <Folder color={theme.iconNormal} />
                            ) : row.type === 'document' ? (
                                <FileWord color={theme.blue500} />
                            ) : (
                                <FilePowerpoint color={theme.bloodOrange500} />
                            )

                        return <GridCellDefault icon={icon} {...props} />
                    },
                },
            },
            {
                id: 'type',
                label: 'Type',
                cell: {
                    value({ row }) {
                        return row.type === 'document'
                            ? 'Document'
                            : row.type === 'slides'
                              ? 'Slides'
                              : 'Folder'
                    },
                },
            },
        ],
        []
    )

    return (
        <Grid
            columns={columns}
            rows={rows}
            selectionMode="none"
            defaultSort={[{ columnId: 'name', direction: 'asc' }]}
            rowDrag={{
                mode: 'parent',
                onDrop({ draggedRowIds, targetParentId }) {
                    setDocumentData((documents) =>
                        documents.map((d) => {
                            if (draggedRowIds.includes(d.id)) {
                                return {
                                    ...d,
                                    parentId: targetParentId,
                                }
                            }
                            return d
                        })
                    )
                },
            }}
        />
    )
}
