<script lang="ts">
  import {
    Background,
    Controls,
    type Edge,
    type Handle,
    MarkerType,
    MiniMap,
    type Node,
    type NodeTypes,
    Panel,
    SvelteFlow,
    useSvelteFlow
  } from '@xyflow/svelte';
  import '@xyflow/svelte/dist/style.css';
  import '../styles/index.ts';
  import {store} from '#store/stores.svelte';
  import {nodeTypes} from './nodes';
  import Toolbar from './Toolbar.svelte';
  import {genShortId} from './utils/IdGen';
  import {useGetNode} from './utils/useGetNode.svelte';
  import {useEnsureParentInNodesBefore} from './utils/useEnsureParentInNodesBefore.svelte';
  import {Textarea} from './base';
  import {useGetEdgesByTarget} from './utils/useGetEdgesByTarget.svelte';
  import {getOptions} from './utils/NodeUtils';
  import CustomNode from './nodes/CustomNode.svelte';
  import {useUpdateEdgeData} from './utils/useUpdateEdgeData.svelte';
  import {Button} from '#components/base/index';
  import {useDeleteEdge} from '#components/utils/useDeleteEdge.svelte';
  import {useGetNodesFromSource} from '#components/utils/useGetNodesFromSource.svelte';
  import {useGetNodeRelativePosition} from '#components/utils/useGetNodeRelativePosition.svelte';
  import {useCopyPasteHandler} from '#components/utils/useCopyPasteHandler.svelte';
  import {onDestroy, onMount} from 'svelte';
  import {isInEditableElement} from '#components/utils/isInEditableElement';

  const { onInit, ...rest } = $props();
    const svelteFlow = useSvelteFlow();

    console.log('props', rest);
    onInit(svelteFlow);

    let showEdgePanel = $state(false);
    let currentEdge = $state<Edge | null>(null);

    const { updateEdgeData } = useUpdateEdgeData();

    const onDragOver = (event: DragEvent) => {
        event.preventDefault();
        if (event.dataTransfer) {
            event.dataTransfer.dropEffect = 'move';
        }
    };

    const onDrop = (event: DragEvent) => {
        event.preventDefault();

        const position = svelteFlow.screenToFlowPosition({
            x: event.clientX - 250,
            y: event.clientY - 100
        });

        const baseNodeJsonString = event.dataTransfer?.getData('application/tinyflow');
        if (!baseNodeJsonString) {
            return;
        }

        const baseNode = JSON.parse(baseNodeJsonString);
        const newNode = {
            id: `node_${genShortId()}`,
            position,
            data: {},
            ...baseNode
        } satisfies Node;

        store.addNode(newNode);
        store.selectNodeOnly(newNode.id);
    };


    const { getNode } = useGetNode();


    const isValidConnection = (conn: any) => {
        const sourceNode = getNode(conn.source)!;
        const targetNode = getNode(conn.target)!;

        // 阻止循环节点连接到父级节点 或者 父级节点连接到子级节点
        if (conn.sourceHandle === 'loop_handle' || sourceNode.parentId) {
            const edges = svelteFlow.getEdges();
            for (let edge of edges) {
                if (edge.target === conn.target) {
                    const edgeSourceNode = getNode(edge.source) as Node;
                    if (conn.sourceHandle === 'loop_handle' && edgeSourceNode.parentId !== sourceNode.id) {
                        return false;
                    }
                    if (sourceNode.parentId && edgeSourceNode.parentId !== sourceNode.parentId) {
                        return false;
                    }
                }
            }
        }

        if (!sourceNode.parentId && targetNode.parentId && targetNode.parentId !== sourceNode.id) {
            return false;
        }

        // 允许链接
        return true;
    };

    const { getNodesFromSource } = useGetNodesFromSource();
    const { getNodeRelativePosition } = useGetNodeRelativePosition();
    const { ensureParentInNodesBefore } = useEnsureParentInNodesBefore();
    const onconnectend = (_: any, state: any) => {
        if (!state.isValid) {
            return;
        }

        const toNode = state.toNode as Node;
        if (toNode.parentId) {
            return;
        }

        const fromNode = state.fromNode as Node;
        const fromHande = state.fromHandle as Handle;

        const newNode = {
            position: { ...toNode.position }
        } as Node;

        if (fromHande.id === 'loop_handle') {
            newNode.parentId = fromNode.id;
        } else if (fromNode.parentId) {
            newNode.parentId = fromNode.parentId;
        }

        if (newNode.parentId) {

            const { x, y } = getNodeRelativePosition(newNode.parentId);

            newNode.position = {
                x: toNode.position.x - x,
                y: toNode.position.y - y
            };

            svelteFlow.updateNode(toNode.id, newNode);

            // 更新目标节点的所有后续的链接节点
            const nodesFromToNode = getNodesFromSource(toNode.id);
            nodesFromToNode.forEach((node) => {
                svelteFlow.updateNode(node.id, {
                    parentId: newNode.parentId,
                    position: {
                        x: node.position.x - x,
                        y: node.position.y - y
                    }
                });
            });

            ensureParentInNodesBefore(newNode.parentId, toNode.id);
        }

        // 显示边面板
        setTimeout(() => {
            store.getEdges().forEach((edge) => {
                if (edge.target === toNode.id && edge.source == fromNode.id) {
                    showEdgePanel = true;
                    currentEdge = edge;
                }
            });
        });
    };


    const { getEdgesByTarget } = useGetEdgesByTarget();
    const onDelete = (params: any) => {
        const deleteEdges = params.edges as Edge[];
        deleteEdges.forEach((edge) => {
            if (edge.id === currentEdge?.id) {
                currentEdge = null;
                showEdgePanel = false;
            }
            const targetNode = getNode(edge.target) as Node;
            if (targetNode && targetNode.parentId) {
                const nodeEdges = getEdgesByTarget(edge.target);
                // const loopNode = getNode(targetNode.parentId) as Node;
                const { x, y } = getNodeRelativePosition(targetNode.parentId);
                if (nodeEdges.length === 0) {
                    svelteFlow.updateNode(targetNode.id, {
                        parentId: undefined,
                        position: {
                            x: targetNode.position.x + x,
                            y: targetNode.position.y + y
                        }
                    });

                    // 更新目标节点的所有后续的链接节点
                    const nodesFromSource = getNodesFromSource(targetNode.id);
                    nodesFromSource.forEach((node) => {
                        svelteFlow.updateNode(node.id, {
                            parentId: undefined,
                            position: {
                                x: node.position.x + x,
                                y: node.position.y + y
                            }
                        });
                    });
                } else {
                    let hasSameParent = false;
                    for (let i = 0; i < nodeEdges.length; i++) {
                        const edge = nodeEdges[i];
                        const sourceNode = getNode(edge.source) as Node;
                        if (sourceNode.parentId || sourceNode.type === 'loopNode') {
                            hasSameParent = true;
                            break;
                        }
                    }
                    if (!hasSameParent) {
                        svelteFlow.updateNode(targetNode.id, {
                            parentId: undefined,
                            position: {
                                x: targetNode.position.x + x,
                                y: targetNode.position.y + y
                            }
                        });

                        // 更新目标节点的所有后续的链接节点
                        const nodesFromSource = getNodesFromSource(targetNode.id);
                        nodesFromSource.forEach((node) => {
                            svelteFlow.updateNode(node.id, {
                                parentId: undefined,
                                position: {
                                    x: node.position.x + x,
                                    y: node.position.y + y
                                }
                            });
                        });
                    }
                }
            }
        });
    };

    const { deleteEdge } = useDeleteEdge();


    const onconnectstart = (event: any, node: any) => {
        // console.log('onconnectstart: ', event, node);
    };


    const onconnect = (event: any) => {
        // console.log('onconnect: ', event);
    };

    const { copyHandler, pasteHandler } = useCopyPasteHandler();

    const handleKeyDown = (e: KeyboardEvent) => {

        if (isInEditableElement()) {
            return;
        }

        if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
            e.preventDefault();
            copyHandler(e);
        }

        if ((e.ctrlKey || e.metaKey) && e.key === 'a') {
            e.preventDefault();
            // 全选所有节点
            store.updateNodes((nodes) => {
                return nodes.map(node => ({ ...node, selected: true }));
            });

            store.updateEdges((edges) => {
                return edges.map(edge => ({ ...edge, selected: true }));
            });
        }
    };

    const handleGlobalPaste = async (event: ClipboardEvent) => {
        // 只在“非输入态”下处理流程图粘贴
        if (isInEditableElement()) {
            return;
        }

        pasteHandler(event);
    };

    onMount(() => {
        window.addEventListener('keydown', handleKeyDown);
        window.addEventListener('paste', handleGlobalPaste);
    });

    onDestroy(() => {
        window.removeEventListener('keydown', handleKeyDown);
        window.removeEventListener('paste', handleGlobalPaste);
    });

    const customNodeTypes = {
        // ...nodeTypes
    } as NodeTypes;

    const customNodes = getOptions().customNodes;
    if (customNodes) {
        for (let key of Object.keys(customNodes)) {
            customNodeTypes[key] = CustomNode as any;
        }
    }

    const onDataChange = getOptions().onDataChange;
    $effect(() => {
        onDataChange?.({
            nodes: store.getNodes(),
            edges: store.getEdges(),
            viewport: store.getViewport()
        });
    });


</script>


<div style="position: relative; height: 100%; width: 100%;overflow: hidden">
    <SvelteFlow nodeTypes={{ ...nodeTypes, ...customNodeTypes}}
                bind:nodes={store.getNodes, store.setNodes}
                bind:edges={store.getEdges, store.setEdges}
                bind:viewport={store.getViewport, store.setViewport}
                ondrop={onDrop}
                ondragover={onDragOver}
                isValidConnection={isValidConnection}
                onconnectend={onconnectend}
                onconnectstart={onconnectstart}
                onconnect={onconnect}
                connectionRadius={50}
                onedgeclick={(e) => {
                    showEdgePanel = true;
                    currentEdge = e.edge;
                }}
                onbeforeconnect={(edge) => {
                    return {
                        ...edge,
                        id:genShortId(),
                    }
                }}
                ondelete={onDelete}
                onclick={(e) => {
                    const el = e.target as HTMLElement;
                    if (el.classList.contains("svelte-flow__edge-interaction")
                        || el.classList.contains('panel-content')
                        || el.closest('.panel-content')){
                     return
                    }
                     showEdgePanel = false;
                     currentEdge = null;
                }}
                defaultEdgeOptions={{
                    // animated: true,
                    // label: 'edge label',
                    markerEnd: {
                        type: MarkerType.ArrowClosed,
                        // color: 'red',
                        width: 20,
                        height: 20
                    }
                }}
    >
        <Background />
        <Controls />
        <MiniMap />

        {#if showEdgePanel}
            <Panel>
                <div class="panel-content">
                    <div>边属性设置</div>
                    <div class="setting-title">边条件设置</div>
                    <div class="setting-item">
                        <Textarea
                            rows={3}
                            placeholder="请输入边条件"
                            style="width: 100%"
                            value={currentEdge?.data?.condition}
                            onchange={(e)=>{
                                if (currentEdge){
                                    updateEdgeData(currentEdge.id, {
                                        condition: e.target?.value
                                    })
                                }
                            }}
                        />
                    </div>
                    <div class="setting-item" style="padding: 8px 0">
                        <Button
                            onclick={() => {
                                deleteEdge(currentEdge?.id)
                                showEdgePanel = false;
                            }}
                        >
                            删除
                        </Button>

                        <Button
                            primary={true}
                            onclick={() => {
                                showEdgePanel = false;
                            }}
                        >
                            保存
                        </Button>
                    </div>
                </div>
            </Panel>
        {/if}
    </SvelteFlow>
    <Toolbar />
</div>

<style>
    .panel-content {
        padding: 10px;
        background-color: #fff;
        border-radius: 5px;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        width: 200px;
        border: 1px solid #efefef;
    }

    .setting-title {
        margin: 10px 0;
        font-size: 12px;
        color: #999;
    }

    .setting-item {
        display: flex;
        gap: 5px;
        align-items: center;
        justify-content: end;
    }
</style>
