<svelte:options customElement={{ props: {} }} />

<script lang="ts">
  import NodeWrapper from '../core/NodeWrapper.svelte';
  import {type Node, useNodesData, useSvelteFlow} from '@xyflow/svelte';
  import {Button, Chosen, Heading, Input, Select, Textarea} from '../base';
  import RefParameterList from '../core/RefParameterList.svelte';
  import {getCurrentNodeId} from '#components/utils/NodeUtils';
  import {fillParameterId, useAddParameter} from '../utils/useAddParameter.svelte';
  import {getOptions} from '../utils/NodeUtils';
  import OutputDefList from '../core/OutputDefList.svelte';
  import ParamTokenEditor from '../core/ParamTokenEditor.svelte';
  import type {TinyflowNodeData} from '#types';
  import {onMount} from 'svelte';

  const props = $props<{
        data: TinyflowNodeData,
        [key: string]: any
    }>();
    const data = $derived(props.data);

    const currentNodeId = getCurrentNodeId();
    let currentNode = useNodesData(currentNodeId);
    const { addParameter } = useAddParameter();
    const flowInstance = useSvelteFlow();
    const { updateNodeData: updateNodeDataInner } = flowInstance;
    const getRestProps = () => {
        const { data: _data, ...rest } = props;
        return rest;
    };
    const editorParameters = $derived.by(() => {
        return (currentNode?.current?.data?.parameters as Array<any>) || data.parameters || [];
    });
    const currentNodeData = $derived.by(() => {
        return (currentNode?.current?.data as Record<string, any>) || (data as Record<string, any>) || {};
    });

    const updateNodeData = (data: Record<string, any>) => {
        updateNodeDataInner(currentNodeId, data);
    };

    const resolveFormValue = (form: any) => {
        return form.resolveValue?.(currentNodeData) ?? currentNodeData?.[form.name] ?? form.defaultValue;
    };

    const resolveFormOptions = (form: any) => {
        return form.resolveOptions?.(currentNodeData) ?? form.options ?? [];
    };

    const updateFormValue = (form: any, nextValue: any) => {
        const patch = form.onValueChange?.(nextValue, currentNodeData) ?? {
            [form.name]: nextValue
        };
        updateNodeData(patch);
    };

    const updateNodeDataByEvent = (form: any, event: Event) => {
        updateFormValue(form, (event.target as any)?.value);
    };

    const buildNode = (nextData = data) => ({
        ...getRestProps(),
        id: currentNodeId,
        data: nextData
    } as Node);

    const externalElement = document.createElement('div') as HTMLElement;
    const options = getOptions();
    const customNode = $derived.by(() => options.customNodes![getRestProps().type as string]);
    const forms = $derived.by(() => customNode.forms);

    onMount(() => {
        customNode.render?.(externalElement, buildNode(), flowInstance);
    });

    let container = $state<HTMLElement | null>(null);
    $effect(() => {
        // 注意：由于 $effect 的 state 自动追踪问题，需要 data.expand 方在 if 里的最前面
        if ((data.expand || customNode.presentation === 'plain') && container) {
            container.append(externalElement);
        }
    });

    $effect(() => {
        if (data) {
            customNode.onUpdate?.(externalElement, buildNode(data));
        }
    });

    $effect(() => {
        if (!data.parameters && customNode.parameters) {
            updateNodeData({
                parameters: fillParameterId(JSON.parse(JSON.stringify(customNode.parameters)))
            });
        }
    });

    $effect(() => {
        if (!data.outputDefs && customNode.outputDefs) {
            updateNodeData({
                outputDefs: fillParameterId(JSON.parse(JSON.stringify(customNode.outputDefs)))
            });
        }
    });

</script>

{#if customNode.presentation === 'plain'}
    <div bind:this={container} style={customNode.rootStyle||""} class={customNode.rootClass}></div>
{:else}
    <NodeWrapper data={{...data, description: customNode.description}} {...getRestProps()}>

        {#snippet icon()}
            {@html customNode.icon}
        {/snippet}

    {#if customNode.parametersEnable !== false}
        <div class="heading">
            <Heading level={3}>输入参数</Heading>

            {#if customNode.parametersAddEnable !== false}
                <Button class="input-btn-more" style="margin-left: auto" onclick={()=>{
                    addParameter(currentNodeId)
                }}>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
                        <path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path>
                    </svg>
                </Button>
            {/if}
        </div>

        <RefParameterList />
    {/if}


    {#if customNode.renderFirst}
        <div bind:this={container} style={customNode.rootStyle||""} class={customNode.rootClass}></div>
    {/if}

    {#if forms}
        {#each forms as form}
            {#if form.type === 'input'}
                <div class="setting-title">{form.label}</div>
                <div class="setting-item">
                    {#if form.templateSupport}
                        <ParamTokenEditor
                            mode="input"
                            placeholder={form.placeholder}
                            style="width: 100%"
                            value={resolveFormValue(form)}
                            parameters={editorParameters}
                            {...form.attrs}
                            oninput={(e:any)=>{
                                updateNodeDataByEvent(form,e)
                            }}
                        />
                    {:else}
                        <Input
                            placeholder={form.placeholder}
                            style="width: 100%"
                            value={resolveFormValue(form)}
                            {...form.attrs}
                            onchange={(e)=>{
                                updateNodeDataByEvent(form,e)
                            }}
                        />
                    {/if}
                </div>
            {:else if form.type === 'textarea'}
                <div class="setting-title">{form.label}</div>
                <div class="setting-item">
                    {#if form.templateSupport}
                        <ParamTokenEditor
                            mode="textarea"
                            rows={3}
                            placeholder={form.placeholder}
                            style="width: 100%"
                            value={resolveFormValue(form)}
                            parameters={editorParameters}
                            {...form.attrs}
                            oninput={(e:any)=>{
                                updateNodeDataByEvent(form,e)
                            }}
                        />
                    {:else}
                        <Textarea
                            rows={3}
                            placeholder={form.placeholder}
                            style="width: 100%"
                            value={resolveFormValue(form)}
                            {...form.attrs}
                            onchange={(e)=>{
                                updateNodeDataByEvent(form,e)
                            }}
                        />
                    {/if}
                </div>
            {:else if form.type === 'slider'}
                <div class="setting-title">{form.label}</div>
                <div class="setting-item">
                    <div class="slider-container">
                        <span>{form.description}: {data[form.name] ?? form.defaultValue}</span>
                        <input
                            class="nodrag"
                            type="range"
                            {...form.attrs}
                            value={data[form.name] ?? form.defaultValue}
                            oninput={(e:any) => updateNodeData({ [form.name]: parseFloat(e.target.value) })}
                        />
                    </div>
                </div>
            {:else if form.type === 'select'}
                <div class="setting-title">{form.label}</div>
                <div class="setting-item">
                    <Select items={resolveFormOptions(form)} style="width: 100%" placeholder={form.placeholder} onSelect={(item)=>{
                      updateFormValue(form, item.value)
                }} value={[resolveFormValue(form)]} />
                </div>
            {:else if form.type === 'chosen'}
                <div class="setting-title">{form.label}</div>
                <div class="setting-item">
                    <Chosen style="width: 100%" placeholder={form.placeholder}
                            buttonText={form.chosen?.buttonText} onChosen={(value,label,event)=>{
                        form.chosen?.onChosen?.(updateNodeData,value,label,event);
                    }} value={data[form.chosen?.valueDataKey||""]} label={data[form.chosen?.labelDataKey||""]} />
                </div>
            {:else if form.type === 'heading'}
                <Heading level={3} mt="10px" {...form.attrs}>{form.label}</Heading>
            {/if}
        {/each}
    {/if}


    {#if !customNode.renderFirst}
        <div bind:this={container} style={customNode.rootStyle||""} class={customNode.rootClass}></div>
    {/if}


    {#if customNode.outputDefsEnable !== false}
        <div class="heading">
            <Heading level={3} mt="10px">输出参数</Heading>

            {#if customNode.outputDefsAddEnable !== false}
                <Button class="input-btn-more" style="margin-left: auto" onclick={()=>{
                    addParameter(currentNodeId,'outputDefs')
                }}>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
                        <path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path>
                    </svg>
                </Button>
            {/if}
        </div>
        <OutputDefList />
    {/if}

    </NodeWrapper>
{/if}

<style>
    .heading {
        display: flex;
        align-items: center;
        margin-bottom: 10px;
    }

    .setting-title {
        font-size: 12px;
        color: var(--tf-text-muted);
        margin-bottom: 4px;
        margin-top: 10px;
    }

    .setting-item {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 10px;
        gap: 10px;
    }

    /* 新增样式 */
    .slider-container {
        width: 100%;
        display: flex;
        flex-direction: column;
        gap: 4px;
    }

    .slider-container span {
        font-size: 12px;
        color: var(--tf-text-secondary);
        display: flex;
        justify-content: space-between;
        align-items: center;
    }

    input[type="range"] {
        width: 100%;
        height: 4px;
        background: var(--tf-slider-track-bg);
        border-radius: 2px;
        outline: none;
        appearance: none;
        -webkit-appearance: none;
    }

    input[type="range"]::-webkit-slider-thumb {
        -webkit-appearance: none;
        width: 14px;
        height: 14px;
        background: var(--tf-slider-thumb-bg);
        border-radius: 50%;
        cursor: pointer;
    }
</style>
