<template>
  <div class="space-y-4">
    <div class="flex items-center justify-between">
      <div>
        <h4 class="text-lg font-semibold">Grafo de trazabilidad</h4>
        <p class="text-sm text-gray-500 dark:text-gray-400">Relaciones insumo → resultado por nivel</p>
      </div>
      <div class="flex gap-2 items-center">
        <UBadge color="blue" variant="subtle" v-if="nodes.length">
          {{ nodes.length }} nodos · {{ edges.length }} aristas
        </UBadge>
      </div>
    </div>

    <div class="relative w-full overflow-x-auto rounded-xl border border-gray-200 dark:border-slate-800 bg-gradient-to-b" :class="bgGradient">
      <svg
        :width="svgWidth"
        :height="svgHeight"
        class="min-w-full"
        :style="{ background: 'transparent' }"
      >
        <defs>
          <filter id="nodeShadow" x="-50%" y="-50%" width="200%" height="200%">
            <feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#0f172a" flood-opacity="0.08" />
          </filter>
          <linearGradient id="edgeGradient" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" :stop-color="edgeColor" stop-opacity="0.8" />
            <stop offset="100%" :stop-color="edgeColor" stop-opacity="0.4" />
          </linearGradient>
        </defs>

        <!-- Grid horizontal y vertical -->
        <g stroke="#e2e8f0" stroke-width="1" :stroke-opacity="colorMode.value === 'dark' ? 0.1 : 0.35">
          <line
            v-for="depth in depthLines"
            :key="`h-${depth}`"
            :x1="0"
            :x2="svgWidth"
            :y1="paddingY + depth * levelGap"
            :y2="paddingY + depth * levelGap"
            stroke-dasharray="4 4"
          />
          <line
            v-for="x in verticalGrid"
            :key="`v-${x}`"
            :x1="x"
            :x2="x"
            y1="0"
            :y2="svgHeight"
            stroke-dasharray="6 6"
          />
        </g>

        <!-- Aristas con codo (vertical + horizontal) -->
        <g
          stroke="url(#edgeGradient)"
          stroke-width="2.5"
          stroke-linecap="round"
          stroke-linejoin="round"
          fill="none"
        >
          <path
            v-for="edge in edges"
            :key="edge.id"
            :d="edge.path"
            class="transition-opacity duration-200"
            :opacity="0.9"
          />
        </g>

        <!-- Nodos -->
        <g>
          <g
            v-for="node in nodes"
            :key="node.id"
            class="transition-transform duration-200"
            filter="url(#nodeShadow)"
          >
            <rect
              :x="node.x - nodeBox.width / 2"
              :y="node.y - nodeBox.height / 2"
              :width="nodeBox.width"
              :height="nodeBox.height"
              rx="14"
              :fill="getNodeFill(node.tipo)"
              :stroke="node.depth === 0 ? accentColor : '#0F172A'"
              :stroke-opacity="node.depth === 0 ? 0.6 : 0.05"
              :stroke-width="node.depth === 0 ? 2 : 1"
            />
            <text
              :x="node.x"
              :y="node.y - 10"
              text-anchor="middle"
              class="font-semibold"
              :fill="textColor"
            >
              {{ node.label }}
            </text>
            <text
              :x="node.x"
              :y="node.y + 10"
              text-anchor="middle"
              class="text-[11px]"
              :fill="subTextColor"
            >
              {{ getTipoLabel(node.tipo) }}
            </text>
            <text
              v-if="node.cantidad_kg"
              :x="node.x"
              :y="node.y + 26"
              text-anchor="middle"
              class="text-[10px]"
              :fill="subTextColor"
            >
              {{ node.cantidad_kg.toLocaleString('es-AR') }} kg
            </text>
          </g>
        </g>
      </svg>
    </div>

    <div class="flex flex-wrap gap-2 text-xs">
      <UBadge color="violet" variant="subtle">Uva</UBadge>
      <UBadge color="green" variant="subtle">Despulpado</UBadge>
      <UBadge color="orange" variant="subtle">Oreado/Presecado</UBadge>
      <UBadge color="blue" variant="subtle">Reposo</UBadge>
      <UBadge color="emerald" variant="subtle">Secado/Mezcla</UBadge>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useColorMode } from '#imports'
import type { TrazabilidadRow } from '~/composables/useLotes'

const props = defineProps<{
  historial: TrazabilidadRow[]
}>()

const paddingX = 80
const paddingY = 60
const levelGap = 140
const nodeBox = { width: 180, height: 64 }
const horizontalGap = 40
const siblingSpacing = nodeBox.width + horizontalGap
const edgePadding = 12
const minElbowHeight = 24
const colorMode = useColorMode()
const bgGradient = computed(() => colorMode.value === 'dark'
  ? 'from-slate-900 to-slate-950'
  : 'from-white to-slate-50')
const edgeColor = computed(() => colorMode.value === 'dark' ? '#94a3b8' : '#94a3b8')
const textColor = computed(() => colorMode.value === 'dark' ? '#e2e8f0' : '#0f172a')
const subTextColor = computed(() => colorMode.value === 'dark' ? '#cbd5e1' : '#334155')
const accentColor = computed(() => colorMode.value === 'dark' ? '#22d3ee' : '#0ea5e9')

type GraphNode = {
  id: string
  label: string
  tipo: string
  cantidad_kg: number | null
  depth: number
  x: number
  y: number
}

const groupedByDepth = computed(() => {
  const levels = new Map<number, TrazabilidadRow[]>()
  for (const row of props.historial) {
    const arr = levels.get(row.profundidad) || []
    arr.push(row)
    levels.set(row.profundidad, arr)
  }
  return levels
})

const nodes = computed<GraphNode[]>(() => {
  const result: GraphNode[] = []
  const levels = groupedByDepth.value
  if (levels.size === 0) return result

  const depthKeys = Array.from(levels.keys()).sort((a, b) => a - b)
  const minCenterX = paddingX + nodeBox.width / 2
  const placedByDepth = new Map<number, GraphNode[]>()

  for (const depth of depthKeys) {
    const rows = [...(levels.get(depth) || [])]

    // Nivel raíz: ubicar centrado y con separación básica
    if (depth === 0) {
      rows.forEach((row, idx) => {
        result.push({
          id: row.lote_id,
          label: row.codigo || row.lote_id.slice(0, 8),
          tipo: row.tipo,
          cantidad_kg: row.cantidad_kg,
          depth,
          x: minCenterX + idx * siblingSpacing,
          y: paddingY + depth * levelGap,
        })
      })
      placedByDepth.set(depth, result.filter((n) => n.depth === depth))
      continue
    }

    const parents = placedByDepth.get(depth - 1) || []
    const childrenByParent = new Map<string, TrazabilidadRow[]>()
    const placedIds = new Set<string>()

    for (const row of rows) {
      const key = row.parent_lote_id || '__orphan__'
      const list = childrenByParent.get(key) || []
      list.push(row)
      childrenByParent.set(key, list)
    }

    let cursor = minCenterX

    // Ubicar hijos agrupados bajo su padre directo
    for (const parent of parents) {
      const children = childrenByParent.get(parent.id)
      if (!children?.length) continue

      const groupWidth = (children.length - 1) * siblingSpacing
      let startX = parent.x - groupWidth / 2

      // Evitar solapamiento con grupos previos
      if (startX < cursor) startX = cursor

      children.forEach((row, idx) => {
        const x = startX + idx * siblingSpacing
        result.push({
          id: row.lote_id,
          label: row.codigo || row.lote_id.slice(0, 8),
          tipo: row.tipo,
          cantidad_kg: row.cantidad_kg,
          depth,
          x,
          y: paddingY + depth * levelGap,
        })
        placedIds.add(row.lote_id)
        cursor = x + siblingSpacing
      })
    }

    // Cualquier nodo huérfano (sin padre en el nivel superior) se coloca al final
    const orphanRows = rows.filter((row) => !placedIds.has(row.lote_id))
    orphanRows.forEach((row, idx) => {
      const x = cursor + idx * siblingSpacing
      result.push({
        id: row.lote_id,
        label: row.codigo || row.lote_id.slice(0, 8),
        tipo: row.tipo,
        cantidad_kg: row.cantidad_kg,
        depth,
        x,
        y: paddingY + depth * levelGap,
      })
      cursor = x + siblingSpacing
    })

    placedByDepth.set(depth, result.filter((n) => n.depth === depth))
  }

  return result
})

const nodeById = computed(() => {
  const map = new Map<string, GraphNode>()
  for (const n of nodes.value) {
    map.set(n.id, n)
  }
  return map
})

const edges = computed(() => {
  const list: {
    id: string
    from: string
    to: string
    path: string
    midY: number
  }[] = []

  for (const row of props.historial) {
    if (!row.parent_lote_id) continue
    const fromNode = nodeById.value.get(row.parent_lote_id)
    const toNode = nodeById.value.get(row.lote_id)
    if (fromNode && toNode) {
      const fromY = fromNode.y + nodeBox.height / 2 + edgePadding
      const toY = toNode.y - nodeBox.height / 2 - edgePadding
      const rawMidY = (fromY + toY) / 2
      const midY = Math.max(
        fromY + minElbowHeight,
        Math.min(toY - minElbowHeight, rawMidY),
      )

      list.push({
        id: `${fromNode.id}-${toNode.id}`,
        from: fromNode.id,
        to: toNode.id,
        midY,
        path: `M ${fromNode.x} ${fromY} L ${fromNode.x} ${midY} L ${toNode.x} ${midY} L ${toNode.x} ${toY}`,
      })
    }
  }

  return list
})

const svgWidth = computed(() => {
  if (!nodes.value.length) return 600
  const xs = nodes.value.map((n) => n.x)
  const minX = Math.min(...xs)
  const maxX = Math.max(...xs)
  const width = (maxX - minX) + paddingX * 2 + nodeBox.width
  return Math.max(width, 600)
})

const svgHeight = computed(() => {
  if (!nodes.value.length) return paddingY * 2 + nodeBox.height
  const maxDepth = Math.max(...nodes.value.map((n) => n.depth), 0)
  return paddingY * 2 + maxDepth * levelGap + nodeBox.height
})

const depthLines = computed(() => {
  if (!nodes.value.length) return []
  const maxDepth = Math.max(...nodes.value.map((n) => n.depth), 0)
  return Array.from({ length: maxDepth + 1 }, (_, i) => i)
})

const verticalGrid = computed(() => {
  if (!nodes.value.length) return []
  const xs = nodes.value.map((n) => n.x)
  const minX = Math.min(...xs)
  const maxX = Math.max(...xs)
  const step = 180
  const coords: number[] = []
  for (let x = minX - step; x <= maxX + step; x += step) {
    coords.push(x)
  }
  return coords
})

const getNodeFill = (tipo: string): string => {
  const palette: Record<string, string> = {
    uva: colorMode.value === 'dark' ? '#4c1d95' : '#F3E8FF',
    despulpado_primera: colorMode.value === 'dark' ? '#166534' : '#DCFCE7',
    despulpado_segunda: colorMode.value === 'dark' ? '#854d0e' : '#FEF9C3',
    despulpado_rechazos: colorMode.value === 'dark' ? '#7f1d1d' : '#FEE2E2',
    oreado: colorMode.value === 'dark' ? '#9a3412' : '#FFEDD5',
    presecado: colorMode.value === 'dark' ? '#92400e' : '#FDE68A',
    reposo: colorMode.value === 'dark' ? '#1d4ed8' : '#DBEAFE',
    secado: colorMode.value === 'dark' ? '#065f46' : '#D1FAE5',
  }
  return palette[tipo] || '#E2E8F0'
}

const getTipoLabel = (tipo: string) => {
  const map: Record<string, string> = {
    uva: 'Uva',
    despulpado_primera: 'Despulpado 1ra',
    despulpado_segunda: 'Despulpado 2da',
    despulpado_rechazos: 'Rechazos',
    oreado: 'Oreado',
    presecado: 'Presecado',
    reposo: 'Reposo',
    secado: 'Secado',
  }
  return map[tipo] || tipo
}
</script>
