<script setup lang="ts">
import { LinePath as svgdomarrowsLinePath } from 'svg-dom-arrows'

import type { ItemTree } from 'match_collector'
import type { Item } from '~/types/cdragon'

defineProps<{
  tree: ItemTree
  parentCount?: number
}>()

const emit = defineEmits<{
  mount: [end: Element]
  refresh: []
  parentReady: []
}>()

const { data: items, pending: itemsLoading } = useFetch<Array<Item>>('/api/cdragon/items', {
  lazy: true, // Don't block rendering
  server: false // Client-side only
})

// Track image loading state
const imagesLoaded = ref(false)

// Create item map reactively
const itemMap = reactive(new Map<number, Item>())
watch(
  items,
  newItems => {
    if (newItems) {
      itemMap.clear()
      for (const item of newItems) {
        itemMap.set(item.id, item)
      }
    }
  },
  { immediate: true }
)

const startTreeItem = useTemplateRef('start')
const arrows: Array<svgdomarrowsLinePath> = []
const pendingChildMounts: Array<Element> = []

// Get the actual icon element for arrow drawing
const startElement = computed(() => startTreeItem.value?.iconElement ?? null)

// Function to wait for an image to load
function waitForImageLoad(imgElement: HTMLImageElement): Promise<void> {
  return new Promise(resolve => {
    if (imgElement.complete) {
      requestAnimationFrame(() => resolve())
      return
    }

    imgElement.addEventListener(
      'load',
      () => {
        requestAnimationFrame(() => resolve())
      },
      { once: true }
    )
    imgElement.addEventListener(
      'error',
      () => {
        requestAnimationFrame(() => resolve())
      },
      { once: true }
    )
  })
}

onMounted(async () => {
  // Wait for next tick to ensure DOM is ready
  await nextTick()

  // Wait for items to be loaded
  await new Promise<void>(resolve => {
    if (!itemsLoading.value) {
      resolve()
    } else {
      const unwatch = watch(itemsLoading, loading => {
        if (!loading) {
          unwatch()
          resolve()
        }
      })
    }
  })

  if (startElement.value) {
    // Wait for the ItemIcon to load its image
    const imgElement = startElement.value.querySelector('img')
    if (imgElement) {
      await waitForImageLoad(imgElement as HTMLImageElement)
    }

    // Now that image is loaded and DOM is ready, draw arrows
    imagesLoaded.value = true

    // Notify children that parent is ready
    emit('parentReady')

    // Draw any pending arrows from children that mounted before we were ready
    if (pendingChildMounts.length > 0) {
      await nextTick()
      for (const childEnd of pendingChildMounts) {
        drawArrow(startElement.value!, childEnd)
      }
      pendingChildMounts.length = 0
    }

    // Use multiple requestAnimationFrame to ensure rendering is complete
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        refreshArrows()
        emit('mount', startElement.value!)
      })
    })
  }
})

onBeforeUpdate(() => {
  for (const arrow of arrows) {
    arrow.release()
  }
  arrows.splice(0, arrows.length)
})

onUpdated(async () => {
  await nextTick()

  if (startElement.value && imagesLoaded.value) {
    // Redraw arrows after DOM update
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        refreshArrows()
        emit('mount', startElement.value!)
      })
    })
  }
})

onUnmounted(() => {
  for (const arrow of arrows) {
    arrow.release()
  }
})

function drawArrow(start: Element, end: Element) {
  if (start == null || end == null) return

  const arrow = new svgdomarrowsLinePath({
    start: {
      element: start,
      position: {
        top: 0.5,
        left: 1
      }
    },
    end: {
      element: end,
      position: {
        top: 0.5,
        left: 0
      }
    },
    style: 'stroke:var(--color-on-surface);stroke-width:2;fill:transparent;pointer-events:none;',
    appendTo: document.body
  })
  arrows.push(arrow)
}

function refreshArrows() {
  // Double requestAnimationFrame to ensure layout is complete
  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      for (const arrow of arrows) {
        arrow.redraw()
      }
    })
  })
}

// Redraw arrows on window resize
addEventListener('resize', _ => {
  refreshArrows()
})
addEventListener('scroll', _ => {
  refreshArrows()
})

function handleSubtreeMount(end: Element) {
  if (startElement.value) {
    if (imagesLoaded.value) {
      // Parent is ready, draw arrow immediately
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          drawArrow(startElement.value!, end)
          refreshArrows()
        })
      })
    } else {
      // Parent not ready yet, store for later
      pendingChildMounts.push(end)
    }
  }
  emit('refresh')
}

function handleParentReady() {
  // Parent became ready, redraw all our arrows
  if (startElement.value && imagesLoaded.value) {
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        refreshArrows()
      })
    })
  }
  // Propagate to children
  emit('parentReady')
}

function handleRefresh() {
  refreshArrows()
  emit('refresh')
}
</script>

<template>
  <div class="item-tree-container">
    <div v-if="tree.data != undefined && tree.data != null" class="item-tree-node">
      <ItemIcon
        v-if="itemMap.get(tree.data)"
        ref="start"
        :item="itemMap.get(tree.data)!"
        :show-pickrate="true"
        :pickrate="parentCount ? tree.count / parentCount : 0"
        :size="48"
        :tags="tree.tags"
        class="item-tree-img"
      />
    </div>

    <div class="item-tree-children">
      <div v-for="child in tree.children" :key="child.data" class="item-tree-child">
        <ItemTree
          :tree="child"
          :parent-count="tree.count"
          @refresh="handleRefresh"
          @mount="end => handleSubtreeMount(end)"
          @parent-ready="handleParentReady"
        />
      </div>
    </div>
  </div>
</template>

<style>
.item-tree-container {
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

.item-tree-node {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: fit-content;
  position: relative;
  z-index: 10;
}

.item-tree-children {
  margin-left: 32px;
  position: relative;
  z-index: 1;
}

.item-tree-child {
  width: fit-content;
  position: relative;
}

/* Mobile responsive */
@media only screen and (max-width: 900px) {
  .item-tree-children {
    margin-left: 20px;
  }
}
</style>
