'use client'

import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { ChevronDown, ChevronUp, HelpCircle, Loader2, Network, Sparkles, Link2 } from 'lucide-react'
import { cn } from '@/lib/utils'
import { useLanguage } from '@/lib/i18n'
import { useNotebooks } from '@/context/notebooks-context'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { NOTE_CHANGE_EVENT } from '@/lib/note-change-sync'
import { openNoteInNewTab } from '@/lib/navigation/open-note'
import {
  SEMANTIC_SIMILARITY_FLOOR,
  semanticOrbitRadius,
  semanticProximityPercent,
  semanticProximityRatio,
} from '@/lib/ai/semantic-proximity'

interface NetworkNote {
  id: string
  title: string | null
  notebookId: string | null
}

interface NetworkLink {
  id: string
  note: NetworkNote
  contextSnippet: string | null
}

interface UnlinkedMention {
  title: string
  snippet: string
}

interface SemanticConnection {
  noteId: string
  title: string | null
  notebookId: string | null
  similarity: number
  excerpt: string
}

interface EmbedHost {
  note: NetworkNote
  blockIds: string[]
}

type OrbitRelationship = 'backlink' | 'outbound' | 'mention' | 'semantic' | 'embed'

interface OrbitNode {
  key: string
  id?: string
  title: string
  color: string
  notebookName: string
  relationship: OrbitRelationship
  snippet?: string | null
  similarity?: number
}

interface NoteNetworkTabProps {
  noteId: string
  noteTitle: string
}

const MAX_GRAPH_NODES = 10
const MAX_LIST_ITEMS = 5
const DEFAULT_COLOR = '#71717A'
const SEMANTIC_COLOR = '#7C3AED'
const CX = 160
const CY = 110

function orbitRadius(relationship: OrbitRelationship): number {
  switch (relationship) {
    case 'outbound': return 52
    case 'backlink': return 68
    case 'embed': return 78
    case 'semantic': return 88
    default: return 94
  }
}

function cleanSnippet(text: string, max = 140): string {
  const plain = text
    .replace(/<[^>]+>/g, ' ')
    .replace(/\|+/g, ' ')
    .replace(/\*{1,2}([^*]+)\*{1,2}/g, '$1')
    .replace(/\s+/g, ' ')
    .trim()
  if (!plain) return ''
  if (plain.length <= max) return plain
  return `${plain.slice(0, max).trim()}…`
}

function SectionHelp({ label, help }: { label: string; help: string }) {
  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <button
          type="button"
          className="p-0.5 rounded-md text-muted-foreground/70 hover:text-foreground hover:bg-muted transition-colors"
          aria-label={label}
        >
          <HelpCircle className="h-3 w-3" />
        </button>
      </TooltipTrigger>
      <TooltipContent side="top" className="max-w-[260px] text-left text-xs leading-relaxed">
        <p className="font-medium mb-1">{label}</p>
        <p className="text-background/85">{help}</p>
      </TooltipContent>
    </Tooltip>
  )
}

function InteractiveOrbitGraph({
  nodes,
  extraCount,
  centerTitle,
  t,
  relationshipLabel,
  similarityFloor,
}: {
  nodes: OrbitNode[]
  extraCount: number
  centerTitle: string
  t: (key: string, params?: Record<string, string | number>) => string
  relationshipLabel: (rel: OrbitRelationship) => string
  similarityFloor: number
}) {
  const svgRef = useRef<SVGSVGElement>(null)
  const [offsets, setOffsets] = useState<Record<string, { dx: number; dy: number }>>({})
  const [activeKey, setActiveKey] = useState<string | null>(null)
  const [hoveredKey, setHoveredKey] = useState<string | null>(null)
  const dragRef = useRef<{
    key: string
    pointerId: number
    startX: number
    startY: number
    baseDx: number
    baseDy: number
    moved: boolean
  } | null>(null)

  const basePositions = useMemo(() => {
    return nodes.map((node, i) => {
      const angle = i * (nodes.length > 0 ? (2 * Math.PI) / nodes.length : 0) - Math.PI / 2
      const r = node.relationship === 'semantic' && node.similarity != null
        ? semanticOrbitRadius(node.similarity, similarityFloor)
        : orbitRadius(node.relationship)
      return {
        key: node.key,
        x: CX + r * Math.cos(angle),
        y: CY + r * 0.88 * Math.sin(angle),
      }
    })
  }, [nodes, similarityFloor])

  const nodePosition = useCallback((key: string, baseX: number, baseY: number) => {
    const o = offsets[key] || { dx: 0, dy: 0 }
    return { x: baseX + o.dx, y: baseY + o.dy }
  }, [offsets])

  const clientToSvg = useCallback((clientX: number, clientY: number) => {
    const svg = svgRef.current
    if (!svg) return { x: 0, y: 0 }
    const pt = svg.createSVGPoint()
    pt.x = clientX
    pt.y = clientY
    const ctm = svg.getScreenCTM()
    if (!ctm) return { x: 0, y: 0 }
    return pt.matrixTransform(ctm.inverse())
  }, [])

  const endDrag = useCallback((pointerId: number) => {
    const d = dragRef.current
    if (!d || d.pointerId !== pointerId) return
    dragRef.current = null
    setActiveKey(null)
  }, [])

  const startDrag = useCallback((
    nodeKey: string,
    pointerId: number,
    clientX: number,
    clientY: number,
    baseDx: number,
    baseDy: number,
  ) => {
    const pt = clientToSvg(clientX, clientY)
    dragRef.current = {
      key: nodeKey,
      pointerId,
      startX: pt.x,
      startY: pt.y,
      baseDx,
      baseDy,
      moved: false,
    }
    setActiveKey(nodeKey)
  }, [clientToSvg])

  useEffect(() => {
    const onMove = (e: PointerEvent) => {
      const d = dragRef.current
      if (!d || e.pointerId !== d.pointerId) return
      e.preventDefault()
      const pt = clientToSvg(e.clientX, e.clientY)
      const dx = d.baseDx + (pt.x - d.startX)
      const dy = d.baseDy + (pt.y - d.startY)
      if (!d.moved && Math.hypot(pt.x - d.startX, pt.y - d.startY) > 4) {
        d.moved = true
      }
      setOffsets(prev => ({ ...prev, [d.key]: { dx, dy } }))
    }

    const onUp = (e: PointerEvent) => {
      const d = dragRef.current
      if (!d || e.pointerId !== d.pointerId) return
      const moved = d.moved
      const node = nodes.find(n => n.key === d.key)
      endDrag(e.pointerId)
      if (!moved && node?.id) {
        openNoteInNewTab(node.id)
      }
    }

    window.addEventListener('pointermove', onMove, { passive: false })
    window.addEventListener('pointerup', onUp)
    window.addEventListener('pointercancel', onUp)
    return () => {
      window.removeEventListener('pointermove', onMove)
      window.removeEventListener('pointerup', onUp)
      window.removeEventListener('pointercancel', onUp)
    }
  }, [clientToSvg, endDrag, nodes])

  const hoveredNode = nodes.find(n => n.key === hoveredKey) || null

  return (
    <div className="rounded-xl border border-border/50 bg-muted/30 overflow-hidden">
      <div className="p-2 pb-0">
        <svg
          ref={svgRef}
          width="100%"
          height="220"
          viewBox="0 0 320 220"
          className="select-none block"
          role="img"
          aria-label={t('documentInfo.network.graphTitle')}
          style={{ touchAction: 'none' }}
        >
          <circle cx={CX} cy={CY} r="96" fill="none" stroke="currentColor" strokeWidth="1" strokeDasharray="3,6" className="text-border/80" />

          {basePositions.map((base, i) => {
            const node = nodes[i]
            const { x, y } = nodePosition(node.key, base.x, base.y)
            const isSemantic = node.relationship === 'semantic'
            const isMention = node.relationship === 'mention'
            const stroke = isSemantic ? SEMANTIC_COLOR : isMention ? '#94A3B8' : '#A47148'
            const proximity = isSemantic && node.similarity != null
              ? semanticProximityRatio(node.similarity, similarityFloor)
              : null

            return (
              <line
                key={`line-${node.key}`}
                x1={CX}
                y1={CY}
                x2={x}
                y2={y}
                stroke={stroke}
                strokeWidth={isSemantic ? 1.2 + (proximity ?? 0) * 2.2 : 1.5}
                strokeDasharray={isMention || isSemantic ? '5,4' : undefined}
                className={isSemantic ? undefined : 'opacity-55'}
                opacity={isSemantic ? 0.35 + (proximity ?? 0) * 0.5 : 0.55}
                style={{ pointerEvents: 'none' }}
              />
            )
          })}

          <circle cx={CX} cy={CY} r="14" fill="#A47148" stroke="var(--background)" strokeWidth="3" style={{ pointerEvents: 'none' }} />
          <circle cx={CX} cy={CY} r="4" fill="white" style={{ pointerEvents: 'none' }} />

          {basePositions.map((base, i) => {
            const node = nodes[i]
            const { x, y } = nodePosition(node.key, base.x, base.y)
            const isActive = activeKey === node.key || hoveredKey === node.key
            const canOpen = !!node.id

            return (
              <g key={node.key}>
                <circle
                  cx={x}
                  cy={y}
                  r={18}
                  fill="transparent"
                  className={cn(canOpen && 'cursor-pointer')}
                  onPointerEnter={() => setHoveredKey(node.key)}
                  onPointerLeave={() => {
                    if (activeKey !== node.key) setHoveredKey(null)
                  }}
                  onPointerDown={(e) => {
                    if (!canOpen) return
                    e.preventDefault()
                    e.stopPropagation()
                    const o = offsets[node.key] || { dx: 0, dy: 0 }
                    startDrag(node.key, e.pointerId, e.clientX, e.clientY, o.dx, o.dy)
                    e.currentTarget.setPointerCapture(e.pointerId)
                  }}
                />
                <circle
                  cx={x}
                  cy={y}
                  r={isActive ? 10 : 7}
                  fill={node.color}
                  stroke={isActive ? 'var(--foreground)' : 'var(--background)'}
                  strokeWidth={2}
                  style={{ pointerEvents: 'none' }}
                />
                <text
                  x={x}
                  y={y + 15}
                  textAnchor="middle"
                  fontSize="7"
                  fill="currentColor"
                  className="text-muted-foreground"
                  style={{ pointerEvents: 'none' }}
                >
                  {node.title.length > 13 ? `${node.title.slice(0, 12)}…` : node.title}
                </text>
              </g>
            )
          })}
        </svg>

        {extraCount > 0 && (
          <p className="text-[9px] text-right text-muted-foreground px-1 -mt-1">
            {t('documentInfo.network.moreNodes', { count: extraCount })}
          </p>
        )}
      </div>

      <div className="mx-2 mb-2 p-2.5 rounded-lg border border-border/40 bg-background min-h-[52px]">
        {hoveredNode ? (
          <div>
            <div className="flex justify-between gap-2 text-[9px] text-muted-foreground mb-0.5">
              <span className="truncate">{hoveredNode.notebookName}</span>
              <span className="font-semibold shrink-0 text-foreground">{relationshipLabel(hoveredNode.relationship)}</span>
            </div>
            <p className="font-medium text-xs leading-snug">{hoveredNode.title}</p>
            {hoveredNode.similarity != null && (
              <p className="text-[10px] text-violet-600 dark:text-violet-400 mt-0.5">
                {t('documentInfo.network.affinityLine', {
                  percentage: semanticProximityPercent(hoveredNode.similarity, similarityFloor),
                })}
              </p>
            )}
            {hoveredNode.snippet && (
              <p className="text-[10px] text-muted-foreground line-clamp-2 mt-1">{cleanSnippet(hoveredNode.snippet)}</p>
            )}
            {hoveredNode.id && (
              <p className="text-[9px] text-muted-foreground mt-1">{t('documentInfo.network.clickToOpen')}</p>
            )}
          </div>
        ) : (
          <p className="text-[10px] text-muted-foreground text-center leading-relaxed">
            {t('documentInfo.network.dragHint')}
          </p>
        )}
      </div>

      <div className="flex flex-wrap gap-x-3 gap-y-1 px-3 pb-2.5 text-[9px] text-muted-foreground border-t border-border/30 pt-2">
        <span className="inline-flex items-center gap-1"><span className="w-2 h-2 rounded-full bg-[#A47148]" />{t('documentInfo.network.legendCenter')}</span>
        <span className="inline-flex items-center gap-1"><span className="w-2 h-2 rounded-full bg-violet-600" />{t('documentInfo.network.legendSemantic')}</span>
        <span className="inline-flex items-center gap-1"><span className="w-2 h-2 rounded-full bg-[#A47148]/60" />{t('documentInfo.network.legendWiki')}</span>
      </div>
    </div>
  )
}

export function NoteNetworkTab({ noteId, noteTitle }: NoteNetworkTabProps) {
  const { t } = useLanguage()
  const { notebooks } = useNotebooks()
  const [loading, setLoading] = useState(true)
  const [backlinks, setBacklinks] = useState<NetworkLink[]>([])
  const [outbound, setOutbound] = useState<NetworkLink[]>([])
  const [unlinkedMentions, setUnlinkedMentions] = useState<UnlinkedMention[]>([])
  const [semanticConnections, setSemanticConnections] = useState<SemanticConnection[]>([])
  const [embedHosts, setEmbedHosts] = useState<EmbedHost[]>([])
  const [consentRequired, setConsentRequired] = useState(false)
  const [similarityFloor, setSimilarityFloor] = useState(SEMANTIC_SIMILARITY_FLOOR)
  const [helpOpen, setHelpOpen] = useState(false)
  const [showAllSemantic, setShowAllSemantic] = useState(false)
  const [showWiki, setShowWiki] = useState(true)
  const [refreshKey, setRefreshKey] = useState(0)

  const loadNetwork = useCallback(() => {
    if (!noteId) return
    setLoading(true)
    fetch(`/api/notes/${noteId}/network`)
      .then(r => r.json())
      .then(data => {
        setBacklinks(data.backlinks || [])
        setOutbound(data.outbound || [])
        setUnlinkedMentions(data.unlinkedMentions || [])
        setSemanticConnections(data.semanticConnections || [])
        setEmbedHosts(data.embedHosts || [])
        setConsentRequired(!!data.consentRequired)
        setSimilarityFloor(typeof data.similarityFloor === 'number' ? data.similarityFloor : SEMANTIC_SIMILARITY_FLOOR)
      })
      .catch(() => {
        setBacklinks([])
        setOutbound([])
        setUnlinkedMentions([])
        setSemanticConnections([])
        setEmbedHosts([])
      })
      .finally(() => setLoading(false))
  }, [noteId])

  useEffect(() => {
    loadNetwork()
  }, [loadNetwork, refreshKey])

  useEffect(() => {
    const onNoteChange = (event: Event) => {
      const detail = (event as CustomEvent).detail
      if (detail?.type === 'updated' && detail.note?.id === noteId) {
        setRefreshKey(k => k + 1)
      }
    }
    window.addEventListener(NOTE_CHANGE_EVENT, onNoteChange)
    return () => window.removeEventListener(NOTE_CHANGE_EVENT, onNoteChange)
  }, [noteId])

  const colorForNotebook = (notebookId: string | null) =>
    notebooks.find(n => n.id === notebookId)?.color || DEFAULT_COLOR

  const notebookNameFor = (notebookId: string | null) =>
    notebooks.find(n => n.id === notebookId)?.name || t('documentInfo.network.unknownNotebook')

  const sortedSemantic = useMemo(
    () => [...semanticConnections].sort((a, b) => b.similarity - a.similarity),
    [semanticConnections]
  )

  const graphNodes = useMemo(() => {
    const nodes: OrbitNode[] = []
    const seen = new Set<string>()

    const push = (node: OrbitNode) => {
      if (node.id) {
        if (seen.has(node.id)) return
        seen.add(node.id)
      }
      nodes.push(node)
    }

    outbound.forEach(link => {
      if (!link.note) return
      push({
        key: `out-${link.id}`,
        id: link.note.id,
        title: link.note.title || t('documentInfo.network.untitled'),
        color: colorForNotebook(link.note.notebookId),
        notebookName: notebookNameFor(link.note.notebookId),
        relationship: 'outbound',
        snippet: link.contextSnippet,
      })
    })

    backlinks.forEach(link => {
      if (!link.note) return
      push({
        key: `in-${link.id}`,
        id: link.note.id,
        title: link.note.title || t('documentInfo.network.untitled'),
        color: colorForNotebook(link.note.notebookId),
        notebookName: notebookNameFor(link.note.notebookId),
        relationship: 'backlink',
        snippet: link.contextSnippet,
      })
    })

    sortedSemantic.forEach(conn => {
      push({
        key: `sem-${conn.noteId}`,
        id: conn.noteId,
        title: conn.title || t('documentInfo.network.untitled'),
        color: SEMANTIC_COLOR,
        notebookName: notebookNameFor(conn.notebookId),
        relationship: 'semantic',
        snippet: conn.excerpt,
        similarity: conn.similarity,
      })
    })

    embedHosts.forEach((host, i) => {
      push({
        key: `embed-${i}-${host.note.id}`,
        id: host.note.id,
        title: host.note.title || t('documentInfo.network.untitled'),
        color: colorForNotebook(host.note.notebookId),
        notebookName: notebookNameFor(host.note.notebookId),
        relationship: 'embed',
        snippet: t('documentInfo.network.embedSnippetOne'),
      })
    })

    return nodes
  }, [sortedSemantic, outbound, backlinks, embedHosts, notebooks, t])

  const orbitNodes = graphNodes.slice(0, MAX_GRAPH_NODES)
  const extraCount = Math.max(0, graphNodes.length - MAX_GRAPH_NODES)

  const relationshipLabel = (rel: OrbitRelationship) => {
    switch (rel) {
      case 'backlink': return t('documentInfo.network.inboundShort')
      case 'outbound': return t('documentInfo.network.outboundShort')
      case 'semantic': return t('documentInfo.network.semanticShort')
      case 'embed': return t('documentInfo.network.embedShort')
      default: return t('documentInfo.network.mentionShort')
    }
  }

  const hasWiki = backlinks.length > 0 || outbound.length > 0 || unlinkedMentions.length > 0
  const visibleSemantic = showAllSemantic ? sortedSemantic : sortedSemantic.slice(0, MAX_LIST_ITEMS)

  if (loading) {
    return (
      <div className="flex flex-col items-center justify-center py-16 gap-2 text-muted-foreground">
        <Loader2 className="h-5 w-5 animate-spin" />
        <p className="text-xs">{t('documentInfo.loading')}</p>
      </div>
    )
  }

  const isEmpty =
    !hasWiki && sortedSemantic.length === 0 && embedHosts.length === 0

  return (
    <div className="p-4 space-y-4">
      <div className="space-y-2">
        <div className="flex items-start justify-between gap-2">
          <div>
            <h4 className="text-sm font-semibold text-foreground">{t('documentInfo.network.graphTitle')}</h4>
            <p className="text-[11px] text-muted-foreground leading-relaxed mt-1">{t('documentInfo.network.intro')}</p>
          </div>
          <button
            type="button"
            onClick={() => setHelpOpen(v => !v)}
            className="shrink-0 p-1.5 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
            aria-expanded={helpOpen}
            aria-label={t('documentInfo.network.helpToggle')}
          >
            <HelpCircle className="h-4 w-4" />
          </button>
        </div>

        {helpOpen && (
          <div className="rounded-xl border border-border/60 bg-muted/30 p-3 space-y-2 text-[11px] text-muted-foreground leading-relaxed">
            <p className="font-medium text-foreground text-xs">{t('documentInfo.network.helpTitle')}</p>
            <p>{t('documentInfo.network.helpGraph')}</p>
            <p>{t('documentInfo.network.helpSemantic')}</p>
            <p>{t('documentInfo.network.helpWiki')}</p>
            <p>{t('documentInfo.network.helpEmbed')}</p>
          </div>
        )}
      </div>

      {isEmpty ? (
        <div className="text-center py-8 px-4 border border-dashed border-border/60 rounded-xl text-muted-foreground space-y-2">
          <Network className="h-7 w-7 mx-auto opacity-30" />
          <p className="text-xs leading-relaxed">{t('documentInfo.network.empty')}</p>
          {consentRequired && (
            <p className="text-[11px] opacity-80">{t('documentInfo.network.consentHint')}</p>
          )}
        </div>
      ) : (
        <>
          {orbitNodes.length > 0 && (
            <InteractiveOrbitGraph
              nodes={orbitNodes}
              extraCount={extraCount}
              centerTitle={noteTitle || t('documentInfo.network.untitled')}
              t={t}
              relationshipLabel={relationshipLabel}
              similarityFloor={similarityFloor}
            />
          )}

          {sortedSemantic.length > 0 && (
            <section className="space-y-2">
              <div className="flex items-center gap-1.5">
                <Sparkles className="h-3.5 w-3.5 text-violet-600" />
                <h5 className="text-xs font-semibold">{t('documentInfo.network.semanticListTitle')}</h5>
                <SectionHelp
                  label={t('documentInfo.network.semanticListTitle')}
                  help={t('documentInfo.network.semanticListHelp')}
                />
                <span className="text-[10px] text-muted-foreground ml-auto">{sortedSemantic.length}</span>
              </div>
              <div className="space-y-1.5">
                {visibleSemantic.map(conn => (
                  <button
                    key={conn.noteId}
                    type="button"
                    onClick={() => openNoteInNewTab(conn.noteId)}
                    className="w-full text-left p-3 rounded-xl border border-violet-500/15 bg-violet-500/[0.04] hover:border-violet-500/35 transition-all"
                  >
                    <div className="flex items-start justify-between gap-2">
                      <span className="text-xs font-medium leading-snug line-clamp-2">{conn.title || t('documentInfo.network.untitled')}</span>
                      <span className="text-[10px] font-semibold text-violet-700 dark:text-violet-300 shrink-0">
                        {semanticProximityPercent(conn.similarity, similarityFloor)} %
                      </span>
                    </div>
                    {conn.excerpt && (
                      <p className="text-[10px] text-muted-foreground mt-1 line-clamp-2 leading-relaxed">{cleanSnippet(conn.excerpt)}</p>
                    )}
                  </button>
                ))}
              </div>
              {sortedSemantic.length > MAX_LIST_ITEMS && (
                <button
                  type="button"
                  onClick={() => setShowAllSemantic(v => !v)}
                  className="text-[11px] text-violet-700 dark:text-violet-300 font-medium hover:underline"
                >
                  {showAllSemantic
                    ? t('documentInfo.network.showLess')
                    : t('documentInfo.network.showMoreSemantic', { count: sortedSemantic.length - MAX_LIST_ITEMS })}
                </button>
              )}
            </section>
          )}

          {embedHosts.length > 0 && (
            <section className="space-y-2">
              <div className="flex items-center gap-1.5">
                <Link2 className="h-3.5 w-3.5 text-muted-foreground" />
                <h5 className="text-xs font-semibold">{t('documentInfo.network.embedListTitle')}</h5>
                <SectionHelp label={t('documentInfo.network.embedListTitle')} help={t('documentInfo.network.helpEmbed')} />
              </div>
              {embedHosts.map(host => (
                <button
                  key={host.note.id}
                  type="button"
                  onClick={() => openNoteInNewTab(host.note.id)}
                  className="w-full text-left p-3 rounded-xl border border-border/50 hover:bg-muted/40 transition-all"
                >
                  <p className="text-xs font-medium line-clamp-2">{host.note.title || t('documentInfo.network.untitled')}</p>
                  <p className="text-[10px] text-muted-foreground mt-0.5">{t('documentInfo.network.embedSnippetOne')}</p>
                </button>
              ))}
            </section>
          )}

          {hasWiki && (
            <section className="space-y-2">
              <button
                type="button"
                onClick={() => setShowWiki(v => !v)}
                className="flex items-center gap-2 w-full text-left group"
              >
                <h5 className="text-xs font-semibold text-muted-foreground group-hover:text-foreground transition-colors">
                  {t('documentInfo.network.wikiSectionTitle')}
                </h5>
                {showWiki ? <ChevronUp className="h-3.5 w-3.5 text-muted-foreground" /> : <ChevronDown className="h-3.5 w-3.5 text-muted-foreground" />}
              </button>

              {showWiki && (
                <div className="space-y-3 pl-0.5">
                  {backlinks.length > 0 && (
                    <WikiList
                      title={t('documentInfo.network.inboundList', { count: backlinks.length })}
                      help={t('documentInfo.network.inboundHelp')}
                      links={backlinks}
                      onOpen={openNoteInNewTab}
                      untitled={t('documentInfo.network.untitled')}
                    />
                  )}
                  {outbound.length > 0 && (
                    <WikiList
                      title={t('documentInfo.network.outboundList', { count: outbound.length })}
                      help={t('documentInfo.network.outboundHelp')}
                      links={outbound}
                      onOpen={openNoteInNewTab}
                      untitled={t('documentInfo.network.untitled')}
                    />
                  )}
                  {unlinkedMentions.length > 0 && (
                    <div className="space-y-1.5">
                      <div className="flex items-center gap-1.5">
                        <p className="text-[10px] uppercase tracking-wider font-semibold text-muted-foreground">
                          {t('documentInfo.network.unlinkedList', { count: unlinkedMentions.length })}
                        </p>
                        <SectionHelp label={t('documentInfo.network.unlinkedListTitle')} help={t('documentInfo.network.unlinkedHelp')} />
                      </div>
                      {unlinkedMentions.map((m, i) => (
                        <div key={i} className="p-2.5 rounded-lg border border-border/40 bg-muted/20 text-[10px] text-muted-foreground leading-relaxed">
                          <span className="font-medium text-foreground">[[{m.title}]]</span>
                          {m.snippet && cleanSnippet(m.snippet) && (
                            <span className="block mt-1 italic">{cleanSnippet(m.snippet, 100)}</span>
                          )}
                        </div>
                      ))}
                    </div>
                  )}
                  {backlinks.length === 0 && outbound.length === 0 && unlinkedMentions.length === 0 && (
                    <p className="text-[11px] text-muted-foreground italic">{t('documentInfo.network.noWikiYet')}</p>
                  )}
                </div>
              )}
            </section>
          )}
        </>
      )}
    </div>
  )
}

function WikiList({
  title,
  help,
  links,
  onOpen,
  untitled,
}: {
  title: string
  help: string
  links: NetworkLink[]
  onOpen: (id: string) => void
  untitled: string
}) {
  return (
    <div className="space-y-1.5">
      <div className="flex items-center gap-1.5">
        <p className="text-[10px] uppercase tracking-wider font-semibold text-muted-foreground">{title}</p>
        <SectionHelp label={title} help={help} />
      </div>
      {links.map(link => (
        <button
          key={link.id}
          type="button"
          onClick={() => onOpen(link.note.id)}
          className="w-full text-left p-2.5 rounded-lg border border-border/40 hover:bg-muted/30 transition-all"
        >
          <p className="text-xs font-medium truncate">{link.note.title || untitled}</p>
          {link.contextSnippet && (
            <p className="text-[10px] text-muted-foreground mt-0.5 line-clamp-2">{cleanSnippet(link.contextSnippet)}</p>
          )}
        </button>
      ))}
    </div>
  )
}
