'use client'

import { useState, useEffect, useMemo, useCallback } from 'react'
import type { FlowNode, FunnelNodeData, OutcomeNodeData, SourceNodeData } from '@/lib/types'

// ─── Layout Types ────────────────────────────────────────

interface RiverLayout {
  sources: SourceLayout[]
  funnels: FunnelLayout[]
  outcomes: OutcomeLayout[]
  sourceWaterfalls: WaterfallLayout[]
  overflowBranches: BranchLayout[]
  spendingWaterfalls: WaterfallLayout[]
  width: number
  height: number
}

interface SourceLayout {
  id: string
  label: string
  flowRate: number
  x: number
  y: number
  width: number
}

interface FunnelLayout {
  id: string
  label: string
  data: FunnelNodeData
  x: number
  y: number
  riverWidth: number
  segmentLength: number
  layer: number
  status: 'healthy' | 'overflow' | 'critical'
}

interface OutcomeLayout {
  id: string
  label: string
  data: OutcomeNodeData
  x: number
  y: number
  poolWidth: number
  fillPercent: number
}

interface WaterfallLayout {
  id: string
  sourceId: string
  targetId: string
  label: string
  percentage: number
  x: number
  xSource: number
  yStart: number
  yEnd: number
  width: number
  riverEndWidth: number
  farEndWidth: number
  direction: 'inflow' | 'outflow'
  color: string
  flowAmount: number
}

interface BranchLayout {
  sourceId: string
  targetId: string
  percentage: number
  x1: number
  y1: number
  x2: number
  y2: number
  width: number
  color: string
}

// ─── Constants ───────────────────────────────────────────

const LAYER_HEIGHT = 160
const WATERFALL_HEIGHT = 120
const GAP = 40
const MIN_RIVER_WIDTH = 24
const MAX_RIVER_WIDTH = 100
const MIN_WATERFALL_WIDTH = 4
const SEGMENT_LENGTH = 200
const POOL_WIDTH = 100
const POOL_HEIGHT = 60
const SOURCE_HEIGHT = 40

const COLORS = {
  sourceWaterfall: '#10b981',
  riverHealthy: ['#0ea5e9', '#06b6d4'],
  riverOverflow: ['#f59e0b', '#fbbf24'],
  riverCritical: ['#ef4444', '#f87171'],
  overflowBranch: '#f59e0b',
  spendingWaterfall: ['#8b5cf6', '#ec4899', '#06b6d4', '#3b82f6', '#10b981', '#6366f1'],
  outcomePool: '#3b82f6',
  bg: '#0f172a',
  text: '#e2e8f0',
  textMuted: '#94a3b8',
}

// ─── Helpers ────────────────────────────────────────────

/** Distribute widths proportionally with a minimum floor */
function distributeWidths(
  percentages: number[],
  totalAvailable: number,
  minWidth: number
): number[] {
  const totalPct = percentages.reduce((s, p) => s + p, 0)
  if (totalPct === 0) return percentages.map(() => minWidth)

  let widths = percentages.map(p => (p / totalPct) * totalAvailable)

  // Enforce minimums: bump small ones up, proportionally reduce large ones
  const belowMin = widths.filter(w => w < minWidth)
  if (belowMin.length > 0 && belowMin.length < widths.length) {
    const deficit = belowMin.reduce((s, w) => s + (minWidth - w), 0)
    const aboveMinTotal = widths.filter(w => w >= minWidth).reduce((s, w) => s + w, 0)

    widths = widths.map(w => {
      if (w < minWidth) return minWidth
      return Math.max(minWidth, w - (w / aboveMinTotal) * deficit)
    })
  }

  return widths
}

// ─── Layout Engine ───────────────────────────────────────

function computeLayout(nodes: FlowNode[]): RiverLayout {
  const funnelNodes = nodes.filter(n => n.type === 'funnel')
  const outcomeNodes = nodes.filter(n => n.type === 'outcome')
  const sourceNodes = nodes.filter(n => n.type === 'source')

  // Build adjacency: which funnels are children (overflow targets) of which
  const overflowTargets = new Set<string>()
  const spendingTargets = new Set<string>()
  const sourceTargets = new Set<string>()

  funnelNodes.forEach(n => {
    const data = n.data as FunnelNodeData
    data.overflowAllocations?.forEach(a => overflowTargets.add(a.targetId))
    data.spendingAllocations?.forEach(a => spendingTargets.add(a.targetId))
  })
  sourceNodes.forEach(n => {
    const data = n.data as SourceNodeData
    data.targetAllocations?.forEach(a => sourceTargets.add(a.targetId))
  })

  // Root funnels = funnels that are NOT overflow targets of other funnels
  const rootFunnels = funnelNodes.filter(n => !overflowTargets.has(n.id))

  // Assign layers via BFS
  const funnelLayers = new Map<string, number>()
  rootFunnels.forEach(n => funnelLayers.set(n.id, 0))

  const queue = [...rootFunnels]
  while (queue.length > 0) {
    const current = queue.shift()!
    const data = current.data as FunnelNodeData
    const parentLayer = funnelLayers.get(current.id) ?? 0
    data.overflowAllocations?.forEach(a => {
      const child = funnelNodes.find(n => n.id === a.targetId)
      if (child && !funnelLayers.has(child.id)) {
        funnelLayers.set(child.id, parentLayer + 1)
        queue.push(child)
      }
    })
  }

  // Find max flow rate for implicit waterfall normalization
  const allFlowRates = funnelNodes.map(n => (n.data as FunnelNodeData).inflowRate || 1)
  const maxFlowRate = Math.max(...allFlowRates, 1)

  // Group funnels by layer, center each layer
  const layerGroups = new Map<number, FlowNode[]>()
  funnelNodes.forEach(n => {
    const layer = funnelLayers.get(n.id) ?? 0
    if (!layerGroups.has(layer)) layerGroups.set(layer, [])
    layerGroups.get(layer)!.push(n)
  })

  const maxLayer = Math.max(...Array.from(layerGroups.keys()), 0)
  const sourceLayerY = GAP
  const funnelStartY = sourceLayerY + SOURCE_HEIGHT + WATERFALL_HEIGHT + GAP

  const funnelLayouts: FunnelLayout[] = []

  for (let layer = 0; layer <= maxLayer; layer++) {
    const layerNodes = layerGroups.get(layer) || []
    const layerY = funnelStartY + layer * (LAYER_HEIGHT + WATERFALL_HEIGHT + GAP)
    const totalWidth = layerNodes.length * SEGMENT_LENGTH + (layerNodes.length - 1) * GAP * 2

    layerNodes.forEach((n, i) => {
      const data = n.data as FunnelNodeData
      const fillRatio = Math.min(1, data.currentValue / (data.maxCapacity || 1))
      const riverWidth = MIN_RIVER_WIDTH + fillRatio * (MAX_RIVER_WIDTH - MIN_RIVER_WIDTH)
      const x = -totalWidth / 2 + i * (SEGMENT_LENGTH + GAP * 2)
      const status: 'healthy' | 'overflow' | 'critical' =
        data.currentValue > data.maxThreshold ? 'overflow' :
        data.currentValue < data.minThreshold ? 'critical' : 'healthy'

      funnelLayouts.push({
        id: n.id,
        label: data.label,
        data,
        x,
        y: layerY,
        riverWidth,
        segmentLength: SEGMENT_LENGTH,
        layer,
        status,
      })
    })
  }

  // Source layouts
  const sourceLayouts: SourceLayout[] = sourceNodes.map((n, i) => {
    const data = n.data as SourceNodeData
    const totalWidth = sourceNodes.length * 120 + (sourceNodes.length - 1) * GAP
    return {
      id: n.id,
      label: data.label,
      flowRate: data.flowRate,
      x: -totalWidth / 2 + i * (120 + GAP),
      y: sourceLayerY,
      width: 120,
    }
  })

  // ─── Source waterfalls (sankey-proportional inflows) ──────

  // First pass: collect all inflows per funnel to compute shares
  const inflowsByFunnel = new Map<string, { sourceNodeId: string; allocIndex: number; flowAmount: number; percentage: number }[]>()

  sourceNodes.forEach(sn => {
    const data = sn.data as SourceNodeData
    data.targetAllocations?.forEach((alloc, i) => {
      const flowAmount = (alloc.percentage / 100) * data.flowRate
      if (!inflowsByFunnel.has(alloc.targetId)) inflowsByFunnel.set(alloc.targetId, [])
      inflowsByFunnel.get(alloc.targetId)!.push({ sourceNodeId: sn.id, allocIndex: i, flowAmount, percentage: alloc.percentage })
    })
  })

  const sourceWaterfalls: WaterfallLayout[] = []

  sourceNodes.forEach(sn => {
    const data = sn.data as SourceNodeData
    const sourceLayout = sourceLayouts.find(s => s.id === sn.id)
    if (!sourceLayout) return

    data.targetAllocations?.forEach((alloc, allocIdx) => {
      const targetLayout = funnelLayouts.find(f => f.id === alloc.targetId)
      if (!targetLayout) return

      const flowAmount = (alloc.percentage / 100) * data.flowRate
      const allInflowsToTarget = inflowsByFunnel.get(alloc.targetId) || []
      const totalInflowToTarget = allInflowsToTarget.reduce((s, i) => s + i.flowAmount, 0)

      // Sankey width: proportional share of the target river width
      const share = totalInflowToTarget > 0 ? flowAmount / totalInflowToTarget : 1
      const riverEndWidth = Math.max(MIN_WATERFALL_WIDTH, share * targetLayout.riverWidth)

      // Far end width: proportional share of source box width
      const farEndWidth = Math.max(MIN_WATERFALL_WIDTH, (alloc.percentage / 100) * sourceLayout.width * 0.8)

      // Position along river top edge: distribute side by side
      const myIndex = allInflowsToTarget.findIndex(i => i.sourceNodeId === sn.id && i.allocIndex === allocIdx)
      const inflowWidths = distributeWidths(
        allInflowsToTarget.map(i => i.flowAmount),
        targetLayout.segmentLength * 0.7,
        MIN_WATERFALL_WIDTH
      )
      const startX = targetLayout.x + targetLayout.segmentLength * 0.15
      let offsetX = 0
      for (let k = 0; k < myIndex; k++) offsetX += inflowWidths[k]
      const riverCenterX = startX + offsetX + inflowWidths[myIndex] / 2

      // Source center x
      const sourceCenterX = sourceLayout.x + sourceLayout.width / 2

      sourceWaterfalls.push({
        id: `src-wf-${sn.id}-${alloc.targetId}`,
        sourceId: sn.id,
        targetId: alloc.targetId,
        label: `${alloc.percentage}%`,
        percentage: alloc.percentage,
        x: riverCenterX,
        xSource: sourceCenterX,
        yStart: sourceLayout.y + SOURCE_HEIGHT,
        yEnd: targetLayout.y,
        width: riverEndWidth,
        riverEndWidth,
        farEndWidth,
        direction: 'inflow',
        color: COLORS.sourceWaterfall,
        flowAmount,
      })
    })
  })

  // Implicit waterfalls for root funnels with inflowRate but no source nodes
  if (sourceNodes.length === 0) {
    rootFunnels.forEach(rn => {
      const data = rn.data as FunnelNodeData
      if (data.inflowRate <= 0) return
      const layout = funnelLayouts.find(f => f.id === rn.id)
      if (!layout) return

      const riverEndWidth = layout.riverWidth
      const farEndWidth = Math.max(MIN_WATERFALL_WIDTH, layout.riverWidth * 0.4)

      sourceWaterfalls.push({
        id: `implicit-wf-${rn.id}`,
        sourceId: 'implicit',
        targetId: rn.id,
        label: `$${Math.floor(data.inflowRate)}/mo`,
        percentage: 100,
        x: layout.x + layout.segmentLength / 2,
        xSource: layout.x + layout.segmentLength / 2,
        yStart: GAP,
        yEnd: layout.y,
        width: riverEndWidth,
        riverEndWidth,
        farEndWidth,
        direction: 'inflow',
        color: COLORS.sourceWaterfall,
        flowAmount: data.inflowRate,
      })
    })
  }

  // ─── Overflow branches (sankey-proportional) ──────

  const overflowBranches: BranchLayout[] = []
  funnelNodes.forEach(n => {
    const data = n.data as FunnelNodeData
    const parentLayout = funnelLayouts.find(f => f.id === n.id)
    if (!parentLayout) return

    data.overflowAllocations?.forEach((alloc) => {
      const childLayout = funnelLayouts.find(f => f.id === alloc.targetId)
      if (!childLayout) return

      const width = Math.max(MIN_WATERFALL_WIDTH, (alloc.percentage / 100) * parentLayout.riverWidth)

      overflowBranches.push({
        sourceId: n.id,
        targetId: alloc.targetId,
        percentage: alloc.percentage,
        x1: parentLayout.x + parentLayout.segmentLength,
        y1: parentLayout.y + parentLayout.riverWidth / 2,
        x2: childLayout.x,
        y2: childLayout.y + childLayout.riverWidth / 2,
        width,
        color: alloc.color || COLORS.overflowBranch,
      })
    })
  })

  // ─── Outcome layouts ──────

  const outcomeY = funnelStartY + (maxLayer + 1) * (LAYER_HEIGHT + WATERFALL_HEIGHT + GAP) + WATERFALL_HEIGHT
  const totalOutcomeWidth = outcomeNodes.length * (POOL_WIDTH + GAP) - GAP
  const outcomeLayouts: OutcomeLayout[] = outcomeNodes.map((n, i) => {
    const data = n.data as OutcomeNodeData
    const fillPercent = data.fundingTarget > 0
      ? Math.min(100, (data.fundingReceived / data.fundingTarget) * 100)
      : 0

    return {
      id: n.id,
      label: data.label,
      data,
      x: -totalOutcomeWidth / 2 + i * (POOL_WIDTH + GAP),
      y: outcomeY,
      poolWidth: POOL_WIDTH,
      fillPercent,
    }
  })

  // ─── Spending waterfalls (sankey-proportional outflows) ──────

  const spendingWaterfalls: WaterfallLayout[] = []
  funnelNodes.forEach(n => {
    const data = n.data as FunnelNodeData
    const parentLayout = funnelLayouts.find(f => f.id === n.id)
    if (!parentLayout) return

    const allocations = data.spendingAllocations || []
    if (allocations.length === 0) return

    // Distribute outflows along the river bottom edge
    const percentages = allocations.map(a => a.percentage)
    const slotWidths = distributeWidths(
      percentages,
      parentLayout.segmentLength * 0.7,
      MIN_WATERFALL_WIDTH
    )
    const riverEndWidths = distributeWidths(
      percentages,
      parentLayout.riverWidth,
      MIN_WATERFALL_WIDTH
    )
    const startX = parentLayout.x + parentLayout.segmentLength * 0.15

    let offsetX = 0
    allocations.forEach((alloc, i) => {
      const outcomeLayout = outcomeLayouts.find(o => o.id === alloc.targetId)
      if (!outcomeLayout) return

      const riverEndWidth = riverEndWidths[i]
      const farEndWidth = Math.max(MIN_WATERFALL_WIDTH, outcomeLayout.poolWidth * 0.6)

      const riverCenterX = startX + offsetX + slotWidths[i] / 2
      offsetX += slotWidths[i]

      const poolCenterX = outcomeLayout.x + outcomeLayout.poolWidth / 2

      spendingWaterfalls.push({
        id: `spend-wf-${n.id}-${alloc.targetId}`,
        sourceId: n.id,
        targetId: alloc.targetId,
        label: `${alloc.percentage}%`,
        percentage: alloc.percentage,
        x: riverCenterX,
        xSource: poolCenterX,
        yStart: parentLayout.y + parentLayout.riverWidth + 4,
        yEnd: outcomeLayout.y,
        width: riverEndWidth,
        riverEndWidth,
        farEndWidth,
        direction: 'outflow',
        color: alloc.color || COLORS.spendingWaterfall[i % COLORS.spendingWaterfall.length],
        flowAmount: (alloc.percentage / 100) * (data.inflowRate || 1),
      })
    })
  })

  // Compute total bounds
  const allX = [
    ...funnelLayouts.map(f => f.x),
    ...funnelLayouts.map(f => f.x + f.segmentLength),
    ...outcomeLayouts.map(o => o.x),
    ...outcomeLayouts.map(o => o.x + o.poolWidth),
    ...sourceLayouts.map(s => s.x),
    ...sourceLayouts.map(s => s.x + s.width),
  ]
  const allY = [
    ...funnelLayouts.map(f => f.y + f.riverWidth),
    ...outcomeLayouts.map(o => o.y + POOL_HEIGHT),
    sourceLayerY,
  ]

  const minX = Math.min(...allX, -100)
  const maxX = Math.max(...allX, 100)
  const maxY = Math.max(...allY, 400)
  const padding = 80

  // Shift everything so minX starts at padding
  const offsetXGlobal = -minX + padding
  const offsetYGlobal = padding

  // Apply offsets
  funnelLayouts.forEach(f => { f.x += offsetXGlobal; f.y += offsetYGlobal })
  outcomeLayouts.forEach(o => { o.x += offsetXGlobal; o.y += offsetYGlobal })
  sourceLayouts.forEach(s => { s.x += offsetXGlobal; s.y += offsetYGlobal })
  sourceWaterfalls.forEach(w => { w.x += offsetXGlobal; w.xSource += offsetXGlobal; w.yStart += offsetYGlobal; w.yEnd += offsetYGlobal })
  overflowBranches.forEach(b => { b.x1 += offsetXGlobal; b.y1 += offsetYGlobal; b.x2 += offsetXGlobal; b.y2 += offsetYGlobal })
  spendingWaterfalls.forEach(w => { w.x += offsetXGlobal; w.xSource += offsetXGlobal; w.yStart += offsetYGlobal; w.yEnd += offsetYGlobal })

  return {
    sources: sourceLayouts,
    funnels: funnelLayouts,
    outcomes: outcomeLayouts,
    sourceWaterfalls,
    overflowBranches,
    spendingWaterfalls,
    width: maxX - minX + padding * 2,
    height: maxY + offsetYGlobal + padding,
  }
}

// ─── SVG Sub-components ──────────────────────────────────

function SankeyWaterfall({ wf }: { wf: WaterfallLayout }) {
  const isInflow = wf.direction === 'inflow'
  const height = wf.yEnd - wf.yStart
  if (height <= 0) return null

  // For inflows: narrow at top (source), wide at bottom (river)
  // For outflows: wide at top (river), narrow at bottom (pool)
  const topWidth = isInflow ? wf.farEndWidth : wf.riverEndWidth
  const bottomWidth = isInflow ? wf.riverEndWidth : wf.farEndWidth
  const topCx = isInflow ? wf.xSource : wf.x
  const bottomCx = isInflow ? wf.x : wf.xSource

  // Bezier control points for the taper curve
  // Inflow: narrow for 55%, then flares in last 45%
  // Outflow: wide for first 20%, then tapers
  const cpFrac1 = isInflow ? 0.55 : 0.2
  const cpFrac2 = isInflow ? 0.75 : 0.45
  const cpY1 = wf.yStart + height * cpFrac1
  const cpY2 = wf.yStart + height * cpFrac2

  const tl = topCx - topWidth / 2
  const tr = topCx + topWidth / 2
  const bl = bottomCx - bottomWidth / 2
  const br = bottomCx + bottomWidth / 2

  const shapePath = [
    `M ${tl} ${wf.yStart}`,
    `C ${tl} ${cpY1}, ${bl} ${cpY2}, ${bl} ${wf.yEnd}`,
    `L ${br} ${wf.yEnd}`,
    `C ${br} ${cpY2}, ${tr} ${cpY1}, ${tr} ${wf.yStart}`,
    `Z`
  ].join(' ')

  const clipId = `sankey-clip-${wf.id}`
  const gradId = `sankey-grad-${wf.id}`

  // Ripple at the wide (river) end
  const rippleCx = isInflow ? bottomCx : topCx
  const rippleCy = isInflow ? wf.yEnd : wf.yStart

  // Bounding box for animated strips
  const pathMinX = Math.min(tl, bl) - 5
  const pathMaxW = Math.max(topWidth, bottomWidth) + 10

  return (
    <g>
      <defs>
        <clipPath id={clipId}>
          <path d={shapePath} />
        </clipPath>
        <linearGradient id={gradId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={wf.color} stopOpacity={isInflow ? 0.85 : 0.5} />
          <stop offset="50%" stopColor={wf.color} stopOpacity={0.65} />
          <stop offset="100%" stopColor={wf.color} stopOpacity={isInflow ? 0.35 : 0.85} />
        </linearGradient>
      </defs>

      {/* Glow behind shape */}
      <path d={shapePath} fill={wf.color} opacity={0.08} />

      {/* Main filled shape */}
      <path d={shapePath} fill={`url(#${gradId})`} />

      {/* Animated water strips inside the shape */}
      <g clipPath={`url(#${clipId})`}>
        {[0, 1, 2].map(i => (
          <rect
            key={i}
            x={pathMinX}
            y={wf.yStart - height}
            width={pathMaxW}
            height={height}
            fill={wf.color}
            opacity={0.12}
            style={{
              animation: `waterFlow ${1.4 + i * 0.3}s linear infinite`,
              animationDelay: `${i * -0.4}s`,
            }}
          />
        ))}
      </g>

      {/* Edge highlight lines along the bezier edges */}
      <path
        d={`M ${tl} ${wf.yStart} C ${tl} ${cpY1}, ${bl} ${cpY2}, ${bl} ${wf.yEnd}`}
        fill="none"
        stroke={wf.color}
        strokeWidth={1}
        opacity={0.3}
        strokeDasharray="4 6"
        style={{ animation: 'riverCurrent 1s linear infinite' }}
      />
      <path
        d={`M ${tr} ${wf.yStart} C ${tr} ${cpY1}, ${br} ${cpY2}, ${br} ${wf.yEnd}`}
        fill="none"
        stroke={wf.color}
        strokeWidth={1}
        opacity={0.3}
        strokeDasharray="4 6"
        style={{ animation: 'riverCurrent 1s linear infinite' }}
      />

      {/* Merge ripples at river junction */}
      {[0, 1, 2].map(i => (
        <ellipse
          key={`merge-${wf.id}-${i}`}
          cx={rippleCx}
          cy={rippleCy}
          rx={2}
          ry={1}
          fill="none"
          stroke={wf.color}
          strokeWidth={1}
          style={{
            animation: `mergeSplash 2s ease-out infinite`,
            animationDelay: `${i * 0.6}s`,
          }}
        />
      ))}

      {/* Droplets at narrow end */}
      {Array.from({ length: Math.max(2, Math.min(5, Math.floor(wf.riverEndWidth / 8))) }, (_, i) => {
        const narrowCx = isInflow ? topCx : bottomCx
        const narrowCy = isInflow ? wf.yStart : wf.yEnd
        const narrowW = isInflow ? topWidth : bottomWidth
        const dx = (Math.random() - 0.5) * narrowW
        return (
          <circle
            key={`drop-${wf.id}-${i}`}
            cx={narrowCx + dx}
            cy={narrowCy + (isInflow ? -5 : 5)}
            r={1.2}
            fill={wf.color}
            opacity={0.5}
            style={{
              animation: `droplet ${1 + Math.random() * 1.5}s ease-in infinite`,
              animationDelay: `${Math.random() * 2}s`,
            }}
          />
        )
      })}

      {/* Label */}
      <text
        x={(topCx + bottomCx) / 2}
        y={wf.yStart + height / 2}
        textAnchor="middle"
        fill={COLORS.text}
        fontSize={9}
        fontWeight={600}
        fontFamily="monospace"
      >
        {wf.label}
      </text>
    </g>
  )
}

function RiverSegment({ funnel }: { funnel: FunnelLayout }) {
  const { id, label, data, x, y, riverWidth, segmentLength, status } = funnel
  const gradColors = status === 'overflow' ? COLORS.riverOverflow :
    status === 'critical' ? COLORS.riverCritical : COLORS.riverHealthy

  const thresholdMinY = y + riverWidth * 0.85
  const thresholdMaxY = y + riverWidth * 0.15

  return (
    <g>
      <defs>
        <linearGradient id={`river-grad-${id}`} x1="0" y1="0" x2="1" y2="0">
          <stop offset="0%" stopColor={gradColors[0]} stopOpacity="0.6" />
          <stop offset="30%" stopColor={gradColors[0]} stopOpacity="0.9" />
          <stop offset="70%" stopColor={gradColors[1]} stopOpacity="0.9" />
          <stop offset="100%" stopColor={gradColors[1]} stopOpacity="0.6" />
        </linearGradient>
      </defs>

      {/* River glow */}
      <rect
        x={x - 4}
        y={y - 4}
        width={segmentLength + 8}
        height={riverWidth + 8}
        rx={riverWidth / 2 + 4}
        fill={gradColors[0]}
        opacity={0.1}
      />

      {/* River body */}
      <rect
        x={x}
        y={y}
        width={segmentLength}
        height={riverWidth}
        rx={riverWidth / 2}
        fill={`url(#river-grad-${id})`}
      />

      {/* Surface current lines */}
      {[0.3, 0.5, 0.7].map((pos, i) => (
        <line
          key={`current-${id}-${i}`}
          x1={x + 10}
          y1={y + riverWidth * pos}
          x2={x + segmentLength - 10}
          y2={y + riverWidth * pos}
          stroke="rgba(255,255,255,0.25)"
          strokeWidth={1}
          strokeDasharray="8 12"
          style={{
            animation: `riverCurrent ${1.5 + i * 0.3}s linear infinite`,
          }}
        />
      ))}

      {/* Shimmer highlight */}
      <ellipse
        cx={x + segmentLength * 0.4}
        cy={y + riverWidth * 0.3}
        rx={segmentLength * 0.15}
        ry={riverWidth * 0.12}
        fill="rgba(255,255,255,0.15)"
        style={{ animation: 'shimmer 3s ease-in-out infinite' }}
      />

      {/* Threshold markers */}
      <line
        x1={x + 5}
        y1={thresholdMaxY}
        x2={x + segmentLength - 5}
        y2={thresholdMaxY}
        stroke="#f59e0b"
        strokeWidth={1.5}
        strokeDasharray="4 3"
        opacity={0.6}
      />
      <text x={x + segmentLength + 6} y={thresholdMaxY + 3} fontSize={7} fill="#f59e0b" fontWeight="bold">MAX</text>

      <line
        x1={x + 5}
        y1={thresholdMinY}
        x2={x + segmentLength - 5}
        y2={thresholdMinY}
        stroke="#ef4444"
        strokeWidth={1.5}
        strokeDasharray="4 3"
        opacity={0.6}
      />
      <text x={x + segmentLength + 6} y={thresholdMinY + 3} fontSize={7} fill="#ef4444" fontWeight="bold">MIN</text>

      {/* Label */}
      <text
        x={x + segmentLength / 2}
        y={y - 10}
        textAnchor="middle"
        fill={COLORS.text}
        fontSize={12}
        fontWeight={700}
      >
        {label}
      </text>

      {/* Value */}
      <text
        x={x + segmentLength / 2}
        y={y + riverWidth / 2 + 4}
        textAnchor="middle"
        fill="white"
        fontSize={13}
        fontWeight={700}
        fontFamily="monospace"
      >
        ${Math.floor(data.currentValue).toLocaleString()}
      </text>

      {/* Status pill */}
      <rect
        x={x + segmentLength / 2 - 16}
        y={y + riverWidth + 6}
        width={32}
        height={14}
        rx={7}
        fill={status === 'overflow' ? '#f59e0b' : status === 'critical' ? '#ef4444' : '#10b981'}
        opacity={0.9}
      />
      <text
        x={x + segmentLength / 2}
        y={y + riverWidth + 16}
        textAnchor="middle"
        fill="white"
        fontSize={7}
        fontWeight={700}
      >
        {status === 'overflow' ? 'OVER' : status === 'critical' ? 'LOW' : 'OK'}
      </text>
    </g>
  )
}

function OverflowBranch({ branch }: { branch: BranchLayout }) {
  const { x1, y1, x2, y2, width, color, percentage } = branch
  const midX = (x1 + x2) / 2
  const midY = (y1 + y2) / 2

  // Curved path connecting two river segments
  const path = `M ${x1} ${y1} C ${x1 + 60} ${y1}, ${x2 - 60} ${y2}, ${x2} ${y2}`

  return (
    <g>
      {/* Glow */}
      <path
        d={path}
        fill="none"
        stroke={color}
        strokeWidth={width + 6}
        opacity={0.1}
        strokeLinecap="round"
      />
      {/* Main branch */}
      <path
        d={path}
        fill="none"
        stroke={color}
        strokeWidth={width}
        opacity={0.7}
        strokeLinecap="round"
        strokeDasharray="10 5"
        style={{ animation: `riverCurrent 1.5s linear infinite` }}
      />
      {/* Flow direction label */}
      <text
        x={midX}
        y={midY - width / 2 - 4}
        textAnchor="middle"
        fill={color}
        fontSize={9}
        fontWeight={600}
        fontFamily="monospace"
      >
        {percentage}%
      </text>
    </g>
  )
}

function OutcomePool({ outcome }: { outcome: OutcomeLayout }) {
  const { id, label, data, x, y, poolWidth, fillPercent } = outcome
  const fillHeight = (fillPercent / 100) * POOL_HEIGHT

  const statusColor =
    data.status === 'completed' ? '#10b981' :
    data.status === 'in-progress' ? '#3b82f6' :
    data.status === 'blocked' ? '#ef4444' : '#64748b'

  return (
    <g>
      {/* Pool container */}
      <rect
        x={x}
        y={y}
        width={poolWidth}
        height={POOL_HEIGHT}
        rx={8}
        fill="rgba(15,23,42,0.8)"
        stroke={statusColor}
        strokeWidth={1.5}
        opacity={0.9}
      />

      {/* Water fill */}
      <defs>
        <clipPath id={`pool-clip-${id}`}>
          <rect x={x + 2} y={y + 2} width={poolWidth - 4} height={POOL_HEIGHT - 4} rx={6} />
        </clipPath>
        <linearGradient id={`pool-grad-${id}`} x1="0" y1="1" x2="0" y2="0">
          <stop offset="0%" stopColor={statusColor} stopOpacity="0.8" />
          <stop offset="100%" stopColor={statusColor} stopOpacity="0.4" />
        </linearGradient>
      </defs>

      <g clipPath={`url(#pool-clip-${id})`}>
        <rect
          x={x + 2}
          y={y + POOL_HEIGHT - fillHeight}
          width={poolWidth - 4}
          height={fillHeight}
          fill={`url(#pool-grad-${id})`}
        />
        {/* Water surface wave */}
        {fillPercent > 5 && (
          <ellipse
            cx={x + poolWidth / 2}
            cy={y + POOL_HEIGHT - fillHeight}
            rx={poolWidth * 0.3}
            ry={2}
            fill="rgba(255,255,255,0.2)"
            style={{ animation: 'waveFloat 2s ease-in-out infinite' }}
          />
        )}
      </g>

      {/* Label */}
      <text
        x={x + poolWidth / 2}
        y={y - 6}
        textAnchor="middle"
        fill={COLORS.text}
        fontSize={9}
        fontWeight={600}
      >
        {label}
      </text>

      {/* Value */}
      <text
        x={x + poolWidth / 2}
        y={y + POOL_HEIGHT / 2 + 2}
        textAnchor="middle"
        fill="white"
        fontSize={9}
        fontWeight={600}
        fontFamily="monospace"
      >
        ${Math.floor(data.fundingReceived).toLocaleString()}
      </text>

      {/* Progress */}
      <text
        x={x + poolWidth / 2}
        y={y + POOL_HEIGHT / 2 + 14}
        textAnchor="middle"
        fill={COLORS.textMuted}
        fontSize={7}
        fontFamily="monospace"
      >
        {Math.round(fillPercent)}% of ${Math.floor(data.fundingTarget).toLocaleString()}
      </text>
    </g>
  )
}

function SourceBox({ source }: { source: SourceLayout }) {
  return (
    <g>
      <rect
        x={source.x}
        y={source.y}
        width={source.width}
        height={SOURCE_HEIGHT}
        rx={8}
        fill="rgba(16,185,129,0.15)"
        stroke="#10b981"
        strokeWidth={1.5}
      />
      <text
        x={source.x + source.width / 2}
        y={source.y + SOURCE_HEIGHT / 2 - 4}
        textAnchor="middle"
        fill="#10b981"
        fontSize={10}
        fontWeight={700}
      >
        {source.label}
      </text>
      <text
        x={source.x + source.width / 2}
        y={source.y + SOURCE_HEIGHT / 2 + 10}
        textAnchor="middle"
        fill="#10b981"
        fontSize={8}
        fontFamily="monospace"
        opacity={0.7}
      >
        ${source.flowRate.toLocaleString()}/mo
      </text>
    </g>
  )
}

// ─── Main Component ──────────────────────────────────────

interface BudgetRiverProps {
  nodes: FlowNode[]
  isSimulating?: boolean
}

export default function BudgetRiver({ nodes, isSimulating = false }: BudgetRiverProps) {
  const [animatedNodes, setAnimatedNodes] = useState<FlowNode[]>(nodes)

  // Update when parent nodes change
  useEffect(() => {
    setAnimatedNodes(nodes)
  }, [nodes])

  // Simulation
  useEffect(() => {
    if (!isSimulating) return

    const interval = setInterval(() => {
      setAnimatedNodes(prev =>
        prev.map(node => {
          if (node.type === 'funnel') {
            const data = node.data as FunnelNodeData
            const change = (Math.random() - 0.45) * 300
            return {
              ...node,
              data: {
                ...data,
                currentValue: Math.max(0, Math.min(data.maxCapacity * 1.1, data.currentValue + change)),
              },
            }
          } else if (node.type === 'outcome') {
            const data = node.data as OutcomeNodeData
            const change = Math.random() * 80
            const newReceived = Math.min(data.fundingTarget * 1.05, data.fundingReceived + change)
            return {
              ...node,
              data: {
                ...data,
                fundingReceived: newReceived,
                status: newReceived >= data.fundingTarget ? 'completed' :
                  data.status === 'not-started' && newReceived > 0 ? 'in-progress' : data.status,
              },
            }
          }
          return node
        })
      )
    }, 500)

    return () => clearInterval(interval)
  }, [isSimulating])

  const layout = useMemo(() => computeLayout(animatedNodes), [animatedNodes])

  return (
    <div className="w-full h-full overflow-auto">
      <svg
        width={layout.width}
        height={layout.height}
        viewBox={`0 0 ${layout.width} ${layout.height}`}
        className="mx-auto"
        style={{ minWidth: layout.width, minHeight: layout.height }}
      >
        {/* Background */}
        <rect width={layout.width} height={layout.height} fill={COLORS.bg} />

        {/* Background stars/dots for atmosphere */}
        {Array.from({ length: 30 }, (_, i) => (
          <circle
            key={`star-${i}`}
            cx={Math.random() * layout.width}
            cy={Math.random() * layout.height}
            r={0.5 + Math.random() * 1}
            fill="rgba(148,163,184,0.2)"
            style={{
              animation: `shimmer ${2 + Math.random() * 3}s ease-in-out infinite`,
              animationDelay: `${Math.random() * 3}s`,
            }}
          />
        ))}

        {/* Layer 1: Source boxes */}
        {layout.sources.map(s => (
          <SourceBox key={s.id} source={s} />
        ))}

        {/* Layer 2: Source waterfalls (sankey inflows pouring into river) */}
        {layout.sourceWaterfalls.map(wf => (
          <SankeyWaterfall key={wf.id} wf={wf} />
        ))}

        {/* Layer 3: River segments */}
        {layout.funnels.map(f => (
          <RiverSegment key={f.id} funnel={f} />
        ))}

        {/* Layer 4: Overflow branches */}
        {layout.overflowBranches.map((b, i) => (
          <OverflowBranch key={`overflow-${i}`} branch={b} />
        ))}

        {/* Layer 5: Spending waterfalls (sankey outflows pouring out) */}
        {layout.spendingWaterfalls.map(wf => (
          <SankeyWaterfall key={wf.id} wf={wf} />
        ))}

        {/* Layer 6: Outcome pools */}
        {layout.outcomes.map(o => (
          <OutcomePool key={o.id} outcome={o} />
        ))}

        {/* Title watermark */}
        <text
          x={layout.width / 2}
          y={layout.height - 20}
          textAnchor="middle"
          fill="rgba(148,163,184,0.15)"
          fontSize={14}
          fontWeight={700}
          letterSpacing={4}
        >
          rFUNDS BUDGET RIVER
        </text>
      </svg>
    </div>
  )
}
