<!-- packages/tinyflow-ui/src/components/base/mixed-input.svelte -->
<script lang="ts">
  import FloatingTrigger from './floating-trigger.svelte';
  import type {SelectItem} from '#types';
  import {nodeIcons} from '../../consts';

  let {
    type = 'fixed',
    textValue = '',
    refValue = '',
    refOptions = [],
    placeholder = '请输入内容',
    onTypeChange,
    onTextChange,
    onRefChange,
    style = ''
  }: {
    type: 'fixed' | 'ref';
    textValue: string;
    refValue: string;
    refOptions: SelectItem[];
    onTypeChange?: (type: 'fixed' | 'ref') => void;
    onTextChange?: (val: string) => void;
    onRefChange?: (val: string) => void;
    placeholder?: string;
    style?: string;
  } = $props();

  let floatingRef: any = $state();
  let hoveredItem: SelectItem | null = $state(null);
  let isOpen = $state(false);
  let selectedItem = $state<SelectItem | null>(null);

  $effect(() => {
     let found: SelectItem | null = null;
     const findItem = (items: SelectItem[]) => {
         for (const it of items) {
             if (it.value === refValue) {
                 found = it;
             }
             if (it.children) findItem(it.children);
         }
     };
     findItem(refOptions);
     selectedItem = found;
  });
  let selectedNodeType = $derived(selectedItem?.nodeType);
  let selectedNodeIcon = $derived(
    selectedNodeType ? nodeIcons[selectedNodeType] : undefined,
  );
  let selectedLabel = $derived(
    selectedItem?.displayLabel || selectedItem?.label || '',
  );

  function closeMenu() {
      floatingRef?.hide();
      isOpen = false;
      hoveredItem = null;
  }

  function handlerOnSelect(item: SelectItem) {
      if (item.selectable !== false) {
          onTypeChange?.('ref');
          onRefChange?.(item.value as string);
          closeMenu();
      } else {
          hoveredItem = item;
      }
  }

  function handleMouseEnter(item: SelectItem) {
      if (item.children && item.children.length > 0) {
          hoveredItem = item;
      }
  }
</script>

{#snippet renderNestedItems(items: SelectItem[], depth = 0)}
    {#each items as item}
        <div class="tf-mixed-item-container" style="padding-left: {depth * 16}px">
            <button class="tf-mixed-item {item.children && item.children.length > 0 ? 'has-children' : ''}"
                    onclick={() => handlerOnSelect(item)}>
                <div class="tf-mixed-label">
                    <div class="tf-mixed-name-wrapper">
                        {#if item.children && item.children.length > 0}
                            <span class="tf-mixed-expand-icon">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"></polyline></svg>
                            </span>
                        {/if}
                        <span class="tf-mixed-name">{item.label}</span>
                    </div>
                    {#if item.dataType}
                        <span class="tf-mixed-type">{item.dataType}</span>
                    {/if}
                </div>
            </button>
        </div>
        {#if item.children && item.children.length > 0}
            <div class="tf-mixed-item-children">
                {@render renderNestedItems(item.children, depth + 1)}
            </div>
        {/if}
    {/each}
{/snippet}

<div class="tf-mixed-input-root" {style}>
    <FloatingTrigger
        bind:this={floatingRef}
        onShow={() => isOpen = true}
        onHide={() => { isOpen = false; hoveredItem = null; }}
        syncWidth={true}
        syncWidthMode="min"
        placement="bottom"
    >
        <!-- Use identical structure to ensure zero visual jumping in box outline -->
        <div class="tf-mixed-wrapper {isOpen ? 'is-focus' : ''}">
            {#if type === 'ref'}
                <!-- Ref view: Styled identical to text view input box -->
                <div class="tf-mixed-box tf-mixed-ref-box">
                    {#if selectedItem}
                        <div class="tf-mixed-sel-val">
                            {#if selectedNodeType && selectedNodeIcon}
                                <span class="tf-mixed-val-icon">
                                    {@html selectedNodeIcon}
                                </span>
                            {/if}
                            <span class="tf-mixed-val-name">{selectedLabel}</span>
                        </div>
                    {:else}
                         <div class="tf-mixed-placeholder">{placeholder}</div>
                    {/if}
                    <!-- Clickable area to trigger dropdown -->
                    <button class="tf-mixed-trigger-btn" aria-label="选择变量" tabindex="-1">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round">
                            <path d="M12 2L20.6603 7V17L12 22L3.33975 17V7L12 2Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
                            <circle cx="12" cy="12" r="2" fill="currentColor"/>
                        </svg>
                    </button>
                </div>
                <!-- Clear Button overlapping -->
                <button class="tf-mixed-clear-btn" onclick={(e) => { e.stopPropagation(); onTypeChange?.('fixed'); onRefChange?.(''); }} title="清空引用">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
                </button>
            {:else}
                <div class="tf-mixed-box tf-mixed-text-box">
                    <input
                        type="text"
                        class="tf-mixed-native-input nopan nodrag"
                        value={textValue}
                        placeholder={placeholder}
                        spellcheck="false"
                        oninput={(e) => onTextChange?.((e.target as HTMLInputElement).value)}
                        onclick={(e) => e.stopPropagation()}
                    />
                    <!-- Trigger button embedded magically over the right edge! Bubbles event up to floating trigger! -->
                    <button class="tf-mixed-trigger-btn" aria-label="选择变量" tabindex="-1">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round">
                            <path d="M12 2L20.6603 7V17L12 22L3.33975 17V7L12 2Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
                            <circle cx="12" cy="12" r="2" fill="currentColor"/>
                        </svg>
                    </button>
                </div>
            {/if}
        </div>

        {#snippet floating()}
             <div class="tf-mixed-dropdown nopan nodrag nowheel {hoveredItem?.children?.length ? 'has-secondary' : ''}">
                  <div class="tf-mixed-list tf-mixed-primary-list">
                      {#each refOptions as item}
                          <button class="tf-mixed-item {hoveredItem?.value === item.value ? 'active' : ''}"
                                  onmouseenter={() => handleMouseEnter(item)}
                                  onclick={() => handlerOnSelect(item)}>
                              <span class="tf-mixed-item-icon">
                                  {#if item.icon}
                                      {@html item.icon}
                                  {:else if item.nodeType && nodeIcons[item.nodeType]}
                                      {@html nodeIcons[item.nodeType]}
                                  {:else if item.children && item.children.length > 0}
                                       <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 7h18M3 12h18M3 17h18"></path></svg>
                                  {/if}
                              </span>
                              <span class="tf-mixed-item-label">{item.label}</span>
                              {#if item.children && item.children.length > 0}
                                  <span class="tf-mixed-item-arrow">
                                      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
                                  </span>
                              {/if}
                          </button>
                      {/each}
                  </div>
                  {#if hoveredItem && hoveredItem.children && hoveredItem.children.length > 0}
                      <div class="tf-mixed-list tf-mixed-secondary-list">
                          {@render renderNestedItems(hoveredItem.children)}
                      </div>
                  {/if}
             </div>
        {/snippet}
    </FloatingTrigger>
</div>

<style lang="less">
  .tf-mixed-input-root {
    display: flex;
    flex: 1;
    min-width: 0;
    width: 100%;
  }

  /* This forces the wrapper generated by floating-trigger to occupy full width */
  :global(.tf-mixed-input-root > div) {
    display: flex;
    flex: 1;
    min-width: 0;
    width: 100%;
  }

  .tf-mixed-wrapper {
    position: relative;
    display: flex;
    flex: 1;
    min-width: 0;
    width: 100%;
    align-items: center;
  }

  .tf-mixed-box {
    display: flex;
    flex: 1;
    min-width: 0;
    width: 100%;
    position: relative;
    border-radius: 6px;
    height: 32px;
    box-sizing: border-box;
  }

  .tf-mixed-ref-box {
    background-color: var(--tf-bg-input);
    border: 1px solid var(--tf-border-color-strong);
    padding: 0 48px 0 12px;
    align-items: center;
    cursor: pointer;
    transition: all 0.2s;
  }

  .tf-mixed-text-box {
    background-color: var(--tf-bg-input);
    border: 1px solid var(--tf-border-color-strong);
    padding: 0 48px 0 12px;
    align-items: center;
    transition: all 0.2s;
  }

  .tf-mixed-wrapper.is-focus .tf-mixed-ref-box,
  .tf-mixed-wrapper.is-focus .tf-mixed-text-box,
  .tf-mixed-ref-box:hover,
  .tf-mixed-text-box:hover {
    border-color: var(--tf-border-color-strong);
  }
  .tf-mixed-wrapper.is-focus .tf-mixed-ref-box,
  .tf-mixed-wrapper.is-focus .tf-mixed-text-box {
    border-color: var(--tf-primary-color);
    outline: none;
    box-shadow: var(--tf-focus-shadow);
  }

  .tf-mixed-sel-val {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    flex: 1;
    overflow: hidden;
  }

  .tf-mixed-val-icon {
    width: 18px;
    height: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--tf-icon-color);
    background: var(--tf-icon-bg);
    border-radius: 4px;
    padding: 2px;
    box-sizing: border-box;

    :global(svg) { width: 12px; height: 12px; }
  }

  .tf-mixed-val-name { color: var(--tf-text-primary); font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; user-select: none; }
  .tf-mixed-placeholder {
    color: var(--tf-text-muted);
    font-size: 13px;
    height: 100%;
    display: flex;
    align-items: center;
    line-height: 1;
  }

  /* Trigger button base styling */
  .tf-mixed-trigger-btn {
    position: absolute;
    width: 26px;
    background: transparent;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--tf-text-muted);
    border-radius: 0 5px 5px 0;
    transition: all 0.2s;
    flex-shrink: 0;

    &:hover { background: var(--tf-bg-tag); color: var(--tf-text-secondary); }
  }

  /* Keep the trigger in the same visual position in both text/ref modes */
  .tf-mixed-text-box .tf-mixed-trigger-btn {
    right: 1px;
    top: 1px;
    bottom: 1px;
  }

  /* Match text mode exactly to avoid 1px horizontal jump after selecting ref */
  .tf-mixed-ref-box .tf-mixed-trigger-btn {
    right: 1px;
    top: 1px;
    bottom: 1px;
  }

  .tf-mixed-clear-btn {
    position: absolute;
    right: 31px;
    width: 20px;
    height: 20px;
    top: 50%;
    transform: translateY(-50%);
    background: var(--tf-bg-active);
    border: none;
    border-radius: 50%;
    color: var(--tf-text-secondary);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    opacity: 0;
    transition: all 0.2s;
    padding: 3px;
    z-index: 10;
  }
  .tf-mixed-clear-btn:hover { background: var(--tf-danger-color); color: var(--tf-bg-surface); }
  .tf-mixed-wrapper:hover .tf-mixed-clear-btn { opacity: 1; }

  .tf-mixed-native-input {
    width: 100%;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    color: var(--tf-text-primary);
    font-size: 13px;
    line-height: 32px;
    height: 32px;
    padding: 0;
    margin: 0;
    box-sizing: border-box;
    appearance: none;
  }

  .tf-mixed-native-input::placeholder {
    color: var(--tf-text-muted);
    line-height: 32px;
  }

  /* Dropdown Styles matched perfectly to Select */
  .tf-mixed-dropdown {
    display: flex;
    background: var(--tf-bg-surface);
    border-radius: 12px;
    box-shadow: var(--tf-shadow-medium);
    border: 1px solid var(--tf-border-color);
    overflow: hidden;
    width: max-content;
    box-sizing: border-box;
    max-height: 480px;
    z-index: 99999;
    margin-top: 5px;
  }
  .tf-mixed-list { display: flex; flex-direction: column; padding: 8px; overflow-y: auto; }
  .tf-mixed-primary-list { width: 100%; flex-shrink: 0; background: var(--tf-bg-surface-alt); }
  .tf-mixed-dropdown.has-secondary .tf-mixed-primary-list { width: auto; min-width: 180px; }
  .tf-mixed-secondary-list { min-width: 220px; background: var(--tf-bg-surface); padding: 12px; border-left: 1px solid var(--tf-bg-muted); animation: slideIn 0.2s ease-out; box-sizing: border-box; }

  @keyframes slideIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }

  .tf-mixed-item {
    display: flex;
    align-items: center;
    padding: 8px 12px;
    border: none;
    background: transparent;
    border-radius: 8px;
    cursor: pointer;
    text-align: left;
    font-size: 13px;
    color: var(--tf-text-primary);
    gap: 10px;
    width: 100%;
    transition: all 0.15s;
    margin-bottom: 2px;

    &:hover { background: var(--tf-bg-muted); }
    &.active { background: var(--tf-primary-soft-bg); color: var(--tf-primary-color); font-weight: 500; }
    &.has-children { background: var(--tf-bg-hover); margin-bottom: 4px; &:hover { background: var(--tf-bg-active); } }
  }

  .tf-mixed-item-icon {
    width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--tf-icon-color); background: var(--tf-icon-bg); border-radius: 5px; padding: 3px; box-sizing: border-box;
    :global(svg) { width: 16px; height: 16px; }
  }
  .tf-mixed-item-label { flex-grow: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .tf-mixed-item-arrow { width: 14px; height: 14px; color: var(--tf-text-muted); }
  .tf-mixed-item-container { position: relative; width: 100%;  }
  .tf-mixed-item-children { position: relative; width: 100%; &::before { content: ''; position: absolute; left: 8px; top: 0; bottom: 0; width: 1px; background: var(--tf-border-color-soft); } }
  .tf-mixed-label { display: flex; justify-content: flex-start; align-items: center; width: 100%; gap: 8px; overflow: hidden;}
  .tf-mixed-name-wrapper { display: flex; align-items: center; gap: 6px; }
  .tf-mixed-expand-icon { width: 12px; height: 12px; color: currentColor; opacity: 0.6; }
  .tf-mixed-name { color: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .tf-mixed-type { background: var(--tf-bg-tag); color: var(--tf-text-secondary); padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; white-space: nowrap; }
</style>
