<script>
  import TemplateIcon from './lib/TemplateIcon.svelte'

  export let nodes = []
  export let expanded = {}
  export let selectedNodeId = ''
  export let level = 0
  export let onSelect = undefined
  export let onToggle = undefined
  export let onContextMenu = undefined
  export let onDrop = undefined

  let autoExpandTimers = {}
  let scrollInterval = null
  let draggedNodeId = ''
  let dragOverNodeId = ''

  const CONTAINER_TYPES = ['folder', 'project', 'client', 'document', 'recipe', 'case']

  import { onDestroy } from 'svelte'

  onDestroy(() => {
    for (const key of Object.keys(autoExpandTimers)) {
      clearTimeout(autoExpandTimers[key])
    }
    if (scrollInterval) clearInterval(scrollInterval)
  })

  function iconKind(node) {
    if (node.type === 'client' || node.template_id === 'client.default') return 'client'
    if (node.type === 'project' || node.template_id === 'project.default') return 'project'
    if (node.type === 'document' || node.template_id === 'document.default') return 'document'
    if (node.type === 'recipe' || node.template_id === 'recipe.default') return 'recipe'
    if (node.type === 'folder' || node.template_id === 'folder.default') return 'folder'
    if (node.type === 'case') return 'case'
    if (node.type === 'note') return 'note'
    if (node.type === 'file') return 'file'
    return 'generic'
  }

  function isContainer(node) {
    return CONTAINER_TYPES.includes(node.type)
  }

  function hasLoadedChildren(node) {
    return node.children !== undefined
  }

  function shouldShowToggle(node) {
    if (hasLoadedChildren(node)) {
      return node.children.length > 0
    }
    return node.has_children === true
  }

  // Build a flat parent map by walking the full tree recursively.
  // Key: node.id, Value: parent node id (or '' for roots)
  function buildParentMap(allNodes) {
    const map = {}
    function walk(list, parentId) {
      for (const n of list) {
        map[n.id] = parentId
        if (n.children) {
          walk(n.children, n.id)
        }
      }
    }
    walk(allNodes, '')
    return map
  }

  // Check if dropping draggedId onto target is allowed.
  // Uses a parent map to detect cycles: walk from target up;
  // if we hit draggedId, it would be a cycle.
  function canDrop(target, draggedId, parentMap) {
    if (!target || !draggedId) return false
    if (draggedId === target.id) return false
    if (!isContainer(target)) return false

    // Walk from target up the parent chain; if we reach draggedId, reject (cycle).
    let cur = target.id
    while (cur) {
      if (cur === draggedId) return false
      cur = parentMap[cur] || ''
    }

    return true
  }

  // Flatten all nodes recursively for parent map construction
  function flattenTree(list) {
    const result = []
    function walk(items) {
      for (const n of items) {
        result.push(n)
        if (n.children) {
          walk(n.children)
        }
      }
    }
    walk(list)
    return result
  }

  function getDraggedId(e) {
    try { return e.dataTransfer.getData('text/plain') } catch { return '' }
  }

  function handleDragStart(e, node) {
    e.stopPropagation()
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('text/plain', node.id)
    draggedNodeId = node.id
  }

  function handleDragOver(e, node) {
    e.preventDefault()
    e.stopPropagation()
    e.dataTransfer.dropEffect = 'move'

    if (dragOverNodeId !== node.id) {
      dragOverNodeId = node.id
    }

    if (shouldShowToggle(node) && !expanded[node.id] && !autoExpandTimers[node.id]) {
      autoExpandTimers[node.id] = setTimeout(() => {
        if (onToggle) onToggle(node.id)
        delete autoExpandTimers[node.id]
      }, 600)
    }

    const area = e.currentTarget.closest('.workspace-tree-area')
    if (area) {
      const areaRect = area.getBoundingClientRect()
      const threshold = 30
      if (e.clientY - areaRect.top < threshold) {
        if (!scrollInterval) scrollInterval = setInterval(() => { area.scrollTop -= 10 }, 50)
      } else if (areaRect.bottom - e.clientY < threshold) {
        if (!scrollInterval) scrollInterval = setInterval(() => { area.scrollTop += 10 }, 50)
      } else {
        if (scrollInterval) { clearInterval(scrollInterval); scrollInterval = null }
      }
    }
  }

  function handleDragLeave(e, node) {
    // Only clear if we're actually leaving this row, not moving to a child element
    const related = e.relatedTarget
    if (related && e.currentTarget.contains(related)) {
      return
    }
    if (dragOverNodeId === node.id) {
      dragOverNodeId = ''
    }
    if (autoExpandTimers[node.id]) {
      clearTimeout(autoExpandTimers[node.id])
      delete autoExpandTimers[node.id]
    }
    if (scrollInterval) { clearInterval(scrollInterval); scrollInterval = null }
  }

  function handleDrop(e, node) {
    e.preventDefault()
    e.stopPropagation()

    if (autoExpandTimers[node.id]) {
      clearTimeout(autoExpandTimers[node.id])
      delete autoExpandTimers[node.id]
    }
    if (scrollInterval) { clearInterval(scrollInterval); scrollInterval = null }

    const allNodes = flattenTree(nodes)
    const parentMap = buildParentMap(allNodes)
    const id = getDraggedId(e)
    if (!canDrop(node, id, parentMap)) {
      dragOverNodeId = ''
      draggedNodeId = ''
      return
    }
    if (onDrop) onDrop(id, node.id)
    dragOverNodeId = ''
    draggedNodeId = ''
  }

  function handleDragEnd() {
    dragOverNodeId = ''
    draggedNodeId = ''
    for (const key of Object.keys(autoExpandTimers)) {
      clearTimeout(autoExpandTimers[key])
      delete autoExpandTimers[key]
    }
    if (scrollInterval) { clearInterval(scrollInterval); scrollInterval = null }
  }

  function handleRowClick(e, node) {
    if (e.target.closest('.tree-toggle') || e.target.closest('.tree-icon')) return
    if (onSelect) onSelect(node)
  }

  function handleRowDblClick(e, node) {
    if (shouldShowToggle(node) && onToggle) onToggle(node.id)
  }

  function handleRowKeydown(e, node) {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      if (onSelect) onSelect(node)
    }
  }

  function handleIconClick(e, node) {
    e.stopPropagation()
    if (shouldShowToggle(node) && onToggle) onToggle(node.id)
  }

  function handleIconKeydown(e, node) {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      e.stopPropagation()
      if (shouldShowToggle(node) && onToggle) onToggle(node.id)
    }
  }

  function handleLabelKeydown(e, node) {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      if (onSelect) onSelect(node)
    }
  }

  function computeDropInfo(allNodes, draggedId, pMap) {
    const info = {}
    function walk(list) {
      for (const n of list) {
        info[n.id] = canDrop(n, draggedId, pMap)
        if (n.children) walk(n.children)
      }
    }
    walk(allNodes)
    return info
  }

  $: flatNodes = flattenTree(nodes)
  $: parentMap = buildParentMap(flatNodes)
  $: dropAllowed = computeDropInfo(flatNodes, draggedNodeId, parentMap)
</script>

<svelte:window on:dragend={handleDragEnd}/>

{#each nodes as node (node.id)}
  <div class="tree-item"
       class:selected={selectedNodeId === node.id}
       class:drop-valid={dragOverNodeId === node.id && dropAllowed[node.id]}
       class:drop-invalid={dragOverNodeId === node.id && !dropAllowed[node.id]}
       style="padding-left: {level * 16 + 4}px"
       draggable="true"
       role="button"
       tabindex="0"
       on:dragstart={(e) => handleDragStart(e, node)}
       on:dragover={(e) => handleDragOver(e, node)}
       on:dragleave={(e) => handleDragLeave(e, node)}
       on:drop={(e) => handleDrop(e, node)}
       on:click={(e) => handleRowClick(e, node)}
       on:keydown={(e) => handleRowKeydown(e, node)}
       on:dblclick={(e) => handleRowDblClick(e, node)}
       on:contextmenu|preventDefault={(e) => onContextMenu && onContextMenu(e, node)}>
    {#if shouldShowToggle(node)}
      <button class="tree-toggle" on:click|stopPropagation={() => onToggle && onToggle(node.id)}>
        <span class="tree-arrow">{expanded[node.id] ? '▾' : '▸'}</span>
      </button>
    {:else}
      <span class="tree-toggle-placeholder"></span>
    {/if}
    <span class="tree-icon" role="button" tabindex="-1"
          on:click={(e) => handleIconClick(e, node)}
          on:keydown={(e) => handleIconKeydown(e, node)}
          on:dblclick|stopPropagation>
      <TemplateIcon kind={iconKind(node)} size={16} />
    </span>
    <span class="tree-label" role="button" tabindex="-1"
          on:click|stopPropagation={() => onSelect && onSelect(node)}
          on:keydown={(e) => handleLabelKeydown(e, node)}>
      {node.title}
    </span>
  </div>
  {#if expanded[node.id] && hasLoadedChildren(node)}
    <svelte:self nodes={node.children || []} {expanded} {selectedNodeId} level={level + 1}
                {onSelect} {onToggle} {onContextMenu} {onDrop} />
  {/if}
{/each}

<style>
  .tree-item {
    display: flex;
    align-items: center;
    height: 32px;
    padding-right: 8px;
    cursor: default;
    font-size: 13px;
    color: #ccc;
    user-select: none;
  }
  .tree-item:hover {
    background: #222233;
  }
  .tree-item.selected {
    background: #2a2a4a;
    color: #fff;
    font-weight: 500;
  }
  .tree-item.drop-valid {
    background: #1a3a1a;
    outline: 1px solid #4ade80;
    outline-offset: -1px;
  }
  .tree-item.drop-invalid {
    background: #3a1a1a;
    outline: 1px solid #ff6b6b;
    outline-offset: -1px;
  }
  .tree-toggle {
    background: none;
    border: none;
    color: #666;
    cursor: pointer;
    padding: 0;
    width: 20px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-family: inherit;
    line-height: 1;
    font-size: 12px;
  }
  .tree-toggle:hover {
    color: #a5b4fc;
  }
  .tree-toggle-placeholder {
    display: inline-block;
    width: 20px;
    flex-shrink: 0;
  }
  .tree-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 32px;
    flex-shrink: 0;
    color: #888;
    margin-right: 4px;
    cursor: pointer;
  }
  .tree-icon:hover {
    color: #a5b4fc;
  }
  .tree-item.selected .tree-icon {
    color: #a5b4fc;
  }
  .tree-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
    line-height: 32px;
  }
</style>
