'use client'

import React, { useEffect, useRef, useState, useCallback } from 'react'
import * as d3 from 'd3'
import { BrainstormSession } from '@/types/brainstorm'

interface WaveCanvasProps {
  session: BrainstormSession
  onNodeSelect: (id: string) => void
  onPositionUpdate?: (id: string, pos: { x: number; y: number }) => void
  selectedNodeId: string | null
  onCreateIdea?: (data: { title: string; parentIdeaId?: string; x: number; y: number }) => void
  remoteMove?: { ideaId: string; x: number; y: number; _seq: number } | null
  manualEditTrigger?: number
  playbackIdeas?: any[] | null
}

const WAVE_COLORS: Record<number, string> = {
  1: '#fb923c',
  2: '#60a5fa',
  3: '#a78bfa',
}

export const WaveCanvas: React.FC<WaveCanvasProps> = ({
  session,
  onNodeSelect,
  onPositionUpdate,
  selectedNodeId,
  onCreateIdea,
  remoteMove,
  manualEditTrigger,
  playbackIdeas,
}) => {
  const svgRef = useRef<SVGSVGElement>(null)
  const containerRef = useRef<HTMLDivElement>(null)
  const nodeRef = useRef<d3.Selection<SVGGElement, any, SVGGElement, unknown> | null>(null)
  const linkRef = useRef<d3.Selection<SVGLineElement, any, SVGGElement, unknown> | null>(null)
  const simulationRef = useRef<d3.Simulation<any, any> | null>(null)
  const transformRef = useRef<d3.ZoomTransform>(d3.zoomIdentity)

  const onNodeSelectRef = useRef(onNodeSelect)
  onNodeSelectRef.current = onNodeSelect
  const onPositionUpdateRef = useRef(onPositionUpdate)
  onPositionUpdateRef.current = onPositionUpdate
  const onCreateIdeaRef = useRef(onCreateIdea)
  onCreateIdeaRef.current = onCreateIdea

  const ideasKey = session?.ideas?.map(i => `${i.id}:${i.status}:${i.positionX}:${i.positionY}`).join('|') || ''
  const sessionId = session?.id || ''

  const [isDark, setIsDark] = useState(false)
  useEffect(() => {
    const el = document.documentElement
    setIsDark(el.classList.contains('dark'))
    const observer = new MutationObserver(() => {
      setIsDark(el.classList.contains('dark'))
    })
    observer.observe(el, { attributes: true, attributeFilter: ['class'] })
    return () => observer.disconnect()
  }, [])

  const [editingNode, setEditingNode] = useState<{
    x: number
    y: number
    parentId: string | null
  } | null>(null)
  const [editText, setEditText] = useState('')
  const inputRef = useRef<HTMLInputElement>(null)

  useEffect(() => {
    if (editingNode && inputRef.current) {
      inputRef.current.focus()
    }
  }, [editingNode])

  useEffect(() => {
    if (!manualEditTrigger || manualEditTrigger === 0) return
    const container = containerRef.current
    if (!container) return
    const rect = container.getBoundingClientRect()
    setEditingNode({
      x: rect.width / 2,
      y: rect.height / 2,
      parentId: null,
    })
    setEditText('')
  }, [manualEditTrigger])

  const toSvgCoords = 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 }
    const svgPt = pt.matrixTransform(ctm.inverse())
    const t = transformRef.current
    return {
      x: (svgPt.x - t.x) / t.k,
      y: (svgPt.y - t.y) / t.k,
    }
  }, [])

  const toScreenCoords = useCallback((svgX: number, svgY: number) => {
    const svg = svgRef.current
    if (!svg) return { x: 0, y: 0 }
    const t = transformRef.current
    const screenX = svgX * t.k + t.x
    const screenY = svgY * t.k + t.y
    const ctm = svg.getScreenCTM()
    if (!ctm) return { x: 0, y: 0 }
    const pt = svg.createSVGPoint()
    pt.x = screenX
    pt.y = screenY
    const screenPt = pt.matrixTransform(ctm)
    return { x: screenPt.x, y: screenPt.y }
  }, [])

  const handleSubmitIdea = useCallback(() => {
    if (!editText.trim() || !editingNode || !onCreateIdeaRef.current) return
    onCreateIdeaRef.current({
      title: editText.trim(),
      parentIdeaId: editingNode.parentId || undefined,
      x: editingNode.x,
      y: editingNode.y,
    })
    setEditingNode(null)
    setEditText('')
  }, [editText, editingNode])

  useEffect(() => {
    if (!svgRef.current || !containerRef.current) return
    if (!session?.id || !session?.ideas) return

    const activeIdeas = playbackIdeas
      ? playbackIdeas.map((pi: any) => ({
          ...pi,
          description: '',
          connectionToSeed: null,
          convertedToNoteId: null,
          relatedNoteIds: null,
          createdBy: null,
          noteRefs: [],
          creator: null,
          createdAt: new Date(),
          sessionId: session.id,
        }))
      : session.ideas

    const width = containerRef.current.clientWidth
    const height = containerRef.current.clientHeight
    const centerX = width / 2
    const centerY = height / 2

    const svg = d3.select(svgRef.current)
    svg.selectAll('*').remove()

    const g = svg.append('g')

    const zoom = d3.zoom<SVGSVGElement, unknown>()
      .scaleExtent([0.1, 5])
      .on('zoom', (event) => {
        g.attr('transform', event.transform)
        transformRef.current = event.transform
      })

    svg.call(zoom)
    svg.call(zoom.transform, d3.zoomIdentity.translate(centerX, centerY).scale(0.8))

    interface D3Node extends d3.SimulationNodeDatum {
      id: string
      type: 'root' | 'idea'
      wave?: number
      title: string
      color: string
      radius: number
      status?: string
      createdByType?: string | null
      creatorInitial?: string
    }

    interface D3Link extends d3.SimulationLinkDatum<D3Node> {
      source: string | D3Node
      target: string | D3Node
      type: 'wave' | 'parent'
    }

    const nodes: D3Node[] = []
    const links: D3Link[] = []

    nodes.push({
      id: 'root',
      type: 'root',
      title: session.seedIdea,
      color: '#141414',
      radius: 40,
      fx: 0,
      fy: 0,
    })

    activeIdeas.forEach((idea) => {
      const creator = (idea as any).creator as { name: string | null } | undefined | null
      nodes.push({
        id: idea.id,
        type: 'idea',
        wave: idea.waveNumber,
        title: idea.title,
        color: WAVE_COLORS[idea.waveNumber as 1 | 2 | 3] || '#94a3b8',
        radius: idea.status === 'dismissed' ? 18 : 28,
        status: idea.status,
        createdByType: idea.createdByType || 'ai',
        creatorInitial: creator?.name ? creator.name.charAt(0).toUpperCase() : undefined,
        x: idea.positionX ?? undefined,
        y: idea.positionY ?? undefined,
      })

      if (idea.parentIdeaId) {
        links.push({ source: idea.parentIdeaId, target: idea.id, type: 'parent' })
      } else {
        links.push({ source: 'root', target: idea.id, type: 'wave' })
      }
    })

    const simulation = d3
      .forceSimulation<D3Node>(nodes)
      .force(
        'link',
        d3
          .forceLink<D3Node, D3Link>(links)
          .id((d) => d.id)
          .distance((d) => {
            if (d.type === 'wave') {
              const targetNode = nodes.find(
                (n) =>
                  n.id ===
                  (typeof d.target === 'string' ? d.target : (d.target as any).id)
              )
              return (targetNode?.wave || 1) * 200
            }
            if (d.type === 'parent') return 180
            return 100
          })
      )
      .force('charge', d3.forceManyBody().strength(-800))
      .force(
        'radial',
        d3
          .forceRadial<D3Node>(
            (d) => {
              if (d.type === 'root') return 0
              return (d.wave || 1) * 200
            },
            0,
            0
          )
          .strength(0.8)
      )
      .force('collision', d3.forceCollide<D3Node>().radius((d) => d.radius + 30))

    simulationRef.current = simulation

    const ringRadii = [200, 400, 600]
    g.selectAll('.ring')
      .data(ringRadii)
      .enter()
      .append('circle')
      .attr('class', 'ring')
      .attr('r', (d) => d)
      .attr('fill', 'none')
      .attr('stroke', isDark ? '#ffffff10' : '#e2e8f0')
      .attr('stroke-width', 1)
      .attr('stroke-dasharray', '4,4')
      .style('opacity', 0.5)

    const link = g
      .append('g')
      .selectAll('line')
      .data(links)
      .enter()
      .append('line')
      .attr('stroke', (d) =>
        d.type === 'wave' ? (isDark ? '#334155' : '#cbd5e1') : (isDark ? '#854d0e' : '#fde047')
      )
      .attr('stroke-width', (d) => (d.type === 'wave' ? 1.5 : 2))
      .attr('stroke-dasharray', (d) => (d.type === 'parent' ? 'none' : '4,4'))

    linkRef.current = link

    const node = g
      .append('g')
      .selectAll('.node')
      .data(nodes)
      .enter()
      .append('g')
      .attr('class', (d) => `node${d.status === 'dismissed' ? ' dismissed' : ''}`)
      .style('cursor', 'pointer')
      .style('opacity', (d) => d.status === 'dismissed' ? 0.3 : 1)
      .attr('data-id', (d) => d.id)
      .on('click', (_event, d) => {
        if (d.type === 'idea') onNodeSelectRef.current?.(d.id)
      })
      .on('dblclick', (event, d) => {
        if (!onCreateIdeaRef.current) return
        event.stopPropagation()
        const parentId = d.type === 'root' ? null : d.id
        const angle = Math.random() * Math.PI * 2
        const dist = 180
        const nx = (d.x || 0) + Math.cos(angle) * dist
        const ny = (d.y || 0) + Math.sin(angle) * dist
        const screen = toScreenCoords(nx, ny)
        const container = containerRef.current
        if (!container) return
        const rect = container.getBoundingClientRect()
        setEditingNode({ x: screen.x - rect.left, y: screen.y - rect.top, parentId })
        setEditText('')
      })
      .call(
        d3
          .drag<SVGGElement, D3Node>()
          .on('start', (event, d) => {
            if (!event.active) simulation.alphaTarget(0.3).restart()
            d.fx = d.x
            d.fy = d.y
          })
          .on('drag', (event, d) => {
            d.fx = event.x
            d.fy = event.y
          })
          .on('end', (event, d) => {
            if (!event.active) simulation.alphaTarget(0)
            d.fx = null
            d.fy = null
            if (d.type === 'idea' && onPositionUpdateRef.current) {
              onPositionUpdateRef.current(d.id, { x: event.x, y: event.y })
            }
          }) as any
      )

    nodeRef.current = node

    node
      .append('circle')
      .attr('r', (d) => d.radius)
      .attr('fill', (d) =>
        d.status === 'converted'
          ? (isDark ? '#064e3b' : '#ecfdf5')
          : d.type === 'root'
            ? (isDark ? '#f97316' : '#141414')
            : (isDark ? '#1e1e1e' : '#fff')
      )
      .attr('stroke', (d) =>
        d.status === 'converted' ? '#10b981' : d.color
      )
      .attr('stroke-width', 2)

    node
      .append('text')
      .attr('dy', (d) =>
        d.type === 'root' ? '.35em' : d.radius + 20
      )
      .attr('text-anchor', 'middle')
      .attr('fill', (d) =>
        d.type === 'root'
          ? '#fff'
          : d.status === 'dismissed'
            ? (isDark ? '#475569' : '#94a3b8')
            : (isDark ? '#e2e8f0' : '#141414')
      )
      .attr(
        'class',
        (d) =>
          d.type === 'root'
            ? 'text-[10px] font-bold pointer-events-none tracking-widest'
            : 'text-[11px] font-bold uppercase tracking-tight pointer-events-none'
      )
      .text((d) =>
        d.type === 'root'
          ? 'SEED'
          : d.title.length > 18
            ? d.title.substring(0, 18) + '...'
            : d.title
      )

    node
      .filter((d) => d.status === 'converted')
      .append('text')
      .attr('x', (d) => d.radius * 0.55)
      .attr('y', (d) => -d.radius * 0.55)
      .attr('text-anchor', 'middle')
      .attr('fill', isDark ? '#34d399' : '#10b981')
      .attr('font-size', '14px')
      .attr('class', 'pointer-events-none')
      .text('✓')

    node
      .filter((d) => {
        if (!d.wave || d.type === 'root') return false
        try {
          const ids: string[] = JSON.parse(
            (session.ideas.find((i) => i.id === d.id)?.relatedNoteIds) || '[]'
          )
          return ids.length > 0
        } catch { return false }
      })
      .append('text')
      .attr('x', (d) => -d.radius * 0.55)
      .attr('y', (d) => -d.radius * 0.55)
      .attr('text-anchor', 'middle')
      .attr('fill', isDark ? '#94a3b8' : '#64748b')
      .attr('font-size', '12px')
      .attr('class', 'pointer-events-none')
      .text('📎')

    node
      .filter((d) => d.type === 'idea' && d.createdByType === 'human')
      .append('circle')
      .attr('cx', (d) => d.radius * 0.6)
      .attr('cy', (d) => d.radius * 0.6)
      .attr('r', 7)
      .attr('fill', '#3b82f6')
      .attr('stroke', isDark ? '#1e1e1e' : '#fff')
      .attr('stroke-width', 1.5)
      .attr('class', 'pointer-events-none')

    node
      .filter((d) => d.type === 'idea' && d.createdByType === 'human')
      .append('text')
      .attr('x', (d) => d.radius * 0.6)
      .attr('y', (d) => d.radius * 0.6 + 3.5)
      .attr('text-anchor', 'middle')
      .attr('fill', '#fff')
      .attr('font-size', '8px')
      .attr('font-weight', 'bold')
      .attr('class', 'pointer-events-none')
      .text((d) => d.creatorInitial || 'U')

    node
      .filter((d) => d.type === 'idea' && d.createdByType === 'ai')
      .append('text')
      .attr('x', (d) => d.radius * 0.55)
      .attr('y', (d) => -d.radius * 0.55 + 4)
      .attr('text-anchor', 'middle')
      .attr('fill', '#a78bfa')
      .attr('font-size', '10px')
      .attr('class', 'pointer-events-none')
      .text('✦')

    g.append('text')
      .attr('text-anchor', 'middle')
      .attr('dy', 80)
      .attr('class', 'text-lg font-serif italic pointer-events-none')
      .attr('fill', isDark ? '#94a3b8' : '#333')
      .text(session.seedIdea.length > 60 ? session.seedIdea.substring(0, 60) + '...' : session.seedIdea)

    svg.on('dblclick', (event) => {
      if (!onCreateIdeaRef.current) return
      if ((event.target as Element).closest('.node')) return
      const container = containerRef.current
      if (!container) return
      const rect = container.getBoundingClientRect()
      setEditingNode({ x: event.clientX - rect.left, y: event.clientY - rect.top, parentId: null })
      setEditText('')
    })

    simulation.on('tick', () => {
      link
        .attr('x1', (d) => (d.source as any).x)
        .attr('y1', (d) => (d.source as any).y)
        .attr('x2', (d) => (d.target as any).x)
        .attr('y2', (d) => (d.target as any).y)

      node.attr('transform', (d) => `translate(${d.x},${d.y})`)
    })

    return () => {
      simulation.stop()
    }
  }, [sessionId, ideasKey, isDark, toSvgCoords, toScreenCoords, playbackIdeas])

  useEffect(() => {
    if (!nodeRef.current) return
    nodeRef.current.selectAll('circle')
      .attr('stroke-width', (d: any) => (d.id === selectedNodeId ? 4 : 2))
      .style('filter', (d: any) =>
        d.id === selectedNodeId
          ? `drop-shadow(0 0 12px ${d.color}cc)`
          : 'none'
      )
  }, [selectedNodeId])

  useEffect(() => {
    if (!remoteMove || !nodeRef.current || !linkRef.current || !simulationRef.current) return
    const sim = simulationRef.current
    const node = nodeRef.current
    const link = linkRef.current

    node.each(function(d: any) {
      if (d.id === remoteMove.ideaId) {
        d.fx = remoteMove.x
        d.fy = remoteMove.y
      }
    })

    sim.alpha(0.3).restart()

    for (let i = 0; i < 30; i++) sim.tick()
    sim.stop()

    link
      .attr('x1', (d: any) => d.source.x)
      .attr('y1', (d: any) => d.source.y)
      .attr('x2', (d: any) => d.target.x)
      .attr('y2', (d: any) => d.target.y)
    node.attr('transform', (d: any) => `translate(${d.x},${d.y})`)
  }, [remoteMove])

  return (
    <div
      ref={containerRef}
      className="w-full h-full relative cursor-grab active:cursor-grabbing bg-[#F8F7F2] dark:bg-[#0A0A0A] bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] dark:bg-[radial-gradient(#ffffff10_1px,transparent_1px)] [background-size:20px_20px]"
    >
      <svg ref={svgRef} className="w-full h-full" />

      {editingNode && (
        <div
          className="absolute z-50 pointer-events-auto"
          style={{
            left: editingNode.x,
            top: editingNode.y,
            transform: 'translate(-50%, 12px)',
          }}
        >
          <div className="w-[260px] bg-white dark:bg-[#1A1A1A] rounded-2xl shadow-2xl border border-black/10 dark:border-white/10 overflow-hidden">
            <div className="px-3.5 pt-3 pb-1 flex items-center gap-2">
              <div className="w-5 h-5 rounded-md bg-memento-blue/10 flex items-center justify-center">
                <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" strokeWidth="2.5" strokeLinecap="round">
                  <path d="M12 5v14M5 12h14" />
                </svg>
              </div>
              <span className="text-[10px] font-bold uppercase tracking-[0.15em] text-foreground/50">
                {editingNode.parentId ? 'Réponse' : 'Nouvelle idée'}
              </span>
            </div>
            <div className="px-3.5 pb-3">
              <input
                ref={inputRef}
                value={editText}
                onChange={(e) => setEditText(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter' && editText.trim()) handleSubmitIdea()
                  if (e.key === 'Escape') { setEditingNode(null); setEditText('') }
                }}
                placeholder={editingNode.parentId ? 'Votre réponse…' : 'Votre idée…'}
                className="w-full px-3 py-2.5 text-sm font-serif bg-black/[0.03] dark:bg-white/[0.06] border border-black/[0.06] dark:border-white/[0.12] rounded-xl outline-none focus:border-memento-blue/50 focus:bg-white dark:focus:bg-white/[0.08] transition-all placeholder:text-foreground/25 text-foreground"
              />
              <div className="flex items-center justify-between mt-1.5 px-0.5">
                <div className="flex items-center gap-2.5">
                  <kbd className="text-[9px] text-foreground/30 font-mono bg-black/[0.04] px-1.5 py-0.5 rounded">↵</kbd>
                  <span className="text-[9px] text-foreground/25">enregistrer</span>
                  <kbd className="text-[9px] text-foreground/30 font-mono bg-black/[0.04] px-1.5 py-0.5 rounded">esc</kbd>
                  <span className="text-[9px] text-foreground/25">annuler</span>
                </div>
                {editingNode.parentId && (
                  <span className="text-[9px] font-medium text-memento-blue/60 flex items-center gap-0.5">
                    → enfant
                  </span>
                )}
              </div>
            </div>
          </div>
          <div className="flex justify-center -mt-[1px]">
            <div className="w-3 h-3 bg-white dark:bg-[#1A1A1A] border-r border-b border-black/10 dark:border-white/10 rotate-45 -translate-y-1.5" />
          </div>
        </div>
      )}

      <div className="absolute bottom-6 left-6 pointer-events-none">
        <p className="text-[10px] font-bold tracking-[0.3em] uppercase text-gray-400 dark:text-gray-600 opacity-60">
          Double-clic pour ajouter une idée
        </p>
      </div>
    </div>
  )
}
