import { useEffect, useState, useCallback } from 'react'
import { useParams } from 'react-router-dom'
import {
  ReactFlow,
  Background,
  Controls,
  MiniMap,
  useNodesState,
  useEdgesState,
  addEdge,
  type Node,
  type Edge,
  type Connection,
  MarkerType,
  Panel,
} from '@xyflow/react'
import '@xyflow/react/dist/style.css'
import dagre from 'dagre'
import { apiFetch } from '@core/lib/api'
import { useAppPath } from '@core/hooks/useAppPath'
import { Button } from '@core/components/ui/button'
import { Badge } from '@core/components/ui/badge'
import { Skeleton } from '@core/components/ui/skeleton'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@core/components/ui/select'
import { RefreshCw, ChevronRight, ZoomIn, Layers } from 'lucide-react'

// ─── LAYOUT ───────────────────────────────────────────────────────────────────

const NODE_WIDTH  = 180
const NODE_HEIGHT = 60
const FILE_WIDTH  = 200
const FILE_HEIGHT = 40

type ViewMode = 'files' | 'chunks' | 'mixed'

function layoutGraph(nodes: Node[], edges: Edge[]): { nodes: Node[]; edges: Edge[] } {
  const g = new dagre.graphlib.Graph()
  g.setDefaultEdgeLabel(() => ({}))
  g.setGraph({ rankdir: 'LR', nodesep: 40, ranksep: 80 })

  for (const node of nodes) {
    g.setNode(node.id, { width: node.data?.isFile ? FILE_WIDTH : NODE_WIDTH, height: node.data?.isFile ? FILE_HEIGHT : NODE_HEIGHT })
  }
  for (const edge of edges) {
    g.setEdge(edge.source, edge.target)
  }

  dagre.layout(g)

  const positionedNodes = nodes.map(node => {
    const pos = g.node(node.id)
    const w = node.data?.isFile ? FILE_WIDTH : NODE_WIDTH
    const h = node.data?.isFile ? FILE_HEIGHT : NODE_HEIGHT
    return { ...node, position: { x: pos.x - w / 2, y: pos.y - h / 2 } }
  })

  return { nodes: positionedNodes, edges }
}

// ─── CUSTOM NODE COLOURS ──────────────────────────────────────────────────────

const CHUNK_TYPE_COLORS: Record<string, string> = {
  function:  '#6366F1', // indigo
  class:     '#8B5CF6', // violet
  interface: '#10B981', // emerald
  type:      '#06B6D4', // cyan
  component: '#F59E0B', // amber
  hook:      '#EC4899', // pink
  route:     '#EF4444', // red
  config:    '#64748B', // slate
}

function nodeColor(node: Node): string {
  if (node.data?.isFile) return '#1E293B'
  if (node.data?.is_dead_code) return '#94A3B8'
  return CHUNK_TYPE_COLORS[node.data?.chunk_type as string] || '#6366F1'
}

// ─── PROJECT DATA → FLOW NODES + EDGES ───────────────────────────────────────

interface ProjectGraph {
  nodes: {
    files: any[]
    chunks: any[]
  }
  edges: {
    imports: { source_id: string; target_id: string; metadata?: any }[]
    calls: { source_id: string; target_id: string; metadata?: any }[]
  }
}

function buildFlowGraph(graph: ProjectGraph, viewMode: ViewMode): { nodes: Node[]; edges: Edge[] } {
  const nodes: Node[] = []
  const edges: Edge[] = []

  if (viewMode === 'files' || viewMode === 'mixed') {
    for (const file of graph.nodes.files) {
      nodes.push({
        id: file.id,
        type: 'default',
        position: { x: 0, y: 0 },
        data: {
          label: file.file_path.split('/').pop() || file.file_path,
          isFile: true,
          file_path: file.file_path,
          language: file.language,
          chunk_count: file.chunk_count,
          is_dead_code: file.is_dead_code,
        },
        style: {
          background: file.is_dead_code ? '#64748B' : '#0F172A',
          color: '#F8FAFC',
          border: `2px solid ${file.is_dead_code ? '#475569' : '#334155'}`,
          borderRadius: '6px',
          fontSize: '11px',
          fontFamily: 'monospace',
          padding: '6px 10px',
          width: FILE_WIDTH,
        },
      })
    }

    for (const imp of graph.edges.imports) {
      edges.push({
        id: `imp-${imp.source_id}-${imp.target_id}`,
        source: imp.source_id,
        target: imp.target_id,
        type: 'smoothstep',
        style: { stroke: '#334155', strokeWidth: 1.5, strokeDasharray: '4,3' },
        markerEnd: { type: MarkerType.ArrowClosed, width: 12, height: 12, color: '#334155' },
        label: imp.metadata?.symbols?.slice(0, 2).join(', ') || '',
        labelStyle: { fontSize: 9, fill: '#94A3B8' },
      })
    }
  }

  if (viewMode === 'chunks' || viewMode === 'mixed') {
    for (const chunk of graph.nodes.chunks) {
      const color = chunk.is_dead_code ? '#94A3B8' : (CHUNK_TYPE_COLORS[chunk.chunk_type] || '#6366F1')
      nodes.push({
        id: chunk.id,
        type: 'default',
        position: { x: 0, y: 0 },
        data: {
          label: chunk.chunk_name,
          isFile: false,
          chunk_type: chunk.chunk_type,
          file_path: chunk.file_path,
          doc_confidence: chunk.doc_confidence,
          boundary_confidence: chunk.boundary_confidence,
          is_dead_code: chunk.is_dead_code,
          stored_hash: chunk.stored_hash,
        },
        style: {
          background: color + '22',
          color: '#1E293B',
          border: `2px solid ${color}`,
          borderRadius: '8px',
          fontSize: '11px',
          fontFamily: 'monospace',
          padding: '8px 12px',
          width: NODE_WIDTH,
          opacity: chunk.is_dead_code ? 0.5 : 1,
        },
      })
    }

    for (const call of graph.edges.calls) {
      edges.push({
        id: `call-${call.source_id}-${call.target_id}`,
        source: call.source_id,
        target: call.target_id,
        type: 'smoothstep',
        style: { stroke: '#6366F1', strokeWidth: 1.5 },
        markerEnd: { type: MarkerType.ArrowClosed, width: 12, height: 12, color: '#6366F1' },
        label: call.metadata?.callee_name || '',
        labelStyle: { fontSize: 9, fill: '#6366F1' },
      })
    }
  }

  return layoutGraph(nodes, edges)
}

// ─── DETAIL PANEL ─────────────────────────────────────────────────────────────

function NodeDetailPanel({
  node,
  projectId,
}: {
  node: Node | null
  projectId: string
}) {
  const appPath = useAppPath()
  if (!node) {
    return (
      <div className="p-3 text-xs text-muted-foreground">
        Click a node to inspect it
      </div>
    )
  }

  const d = node.data as any

  if (d.isFile) {
    return (
      <div className="p-3 space-y-2 text-xs">
        <div className="font-mono font-bold truncate" title={d.file_path}>{d.file_path}</div>
        <div className="text-muted-foreground">{d.language} · {d.chunk_count ?? '?'} chunks</div>
        {d.is_dead_code && <Badge variant="destructive" className="text-xs">Dead Code</Badge>}
      </div>
    )
  }

  return (
    <div className="p-3 space-y-2 text-xs">
      <div className="font-mono font-bold">{d.label}</div>
      <div className="text-muted-foreground truncate">{d.file_path}</div>
      <div className="flex gap-1 flex-wrap">
        <Badge variant="secondary" className="text-xs">{d.chunk_type}</Badge>
        {d.is_dead_code && <Badge variant="destructive" className="text-xs">Dead</Badge>}
        {d.doc_confidence === 'human_verified' && <Badge variant="default" className="text-xs bg-green-600">Verified</Badge>}
        {d.doc_confidence === 'ai_generated' && <Badge variant="outline" className="text-xs">AI Docs</Badge>}
        {!d.stored_hash && <Badge variant="outline" className="text-xs text-amber-600 border-amber-300">No Hash</Badge>}
      </div>
      <a
        href={appPath(`/cil/${projectId}/chunks/${node.id}`)}
        className="block text-indigo-600 hover:underline font-medium"
      >
        Open Contract →
      </a>
    </div>
  )
}

// ─── MAIN PAGE ────────────────────────────────────────────────────────────────

export default function CILGraphPage() {
  const { id: projectId } = useParams<{ id: string }>()
  const appPath = useAppPath()
  const [nodes, setNodes, onNodesChange] = useNodesState([])
  const [edges, setEdges, onEdgesChange] = useEdgesState([])
  const [loading, setLoading] = useState(true)
  const [viewMode, setViewMode] = useState<ViewMode>('chunks')
  const [selectedNode, setSelectedNode] = useState<Node | null>(null)
  const [stats, setStats] = useState({ files: 0, chunks: 0, imports: 0, calls: 0 })
  const [projectTitle, setProjectTitle] = useState('')

  const loadGraph = useCallback(async () => {
    if (!projectId) return
    setLoading(true)
    try {
      const [graphRes, projectRes] = await Promise.all([
        apiFetch(`/api/custom_cil-graph?action=get_project_graph&project_id=${projectId}`),
        apiFetch(`/api/custom_cil-project?action=get&id=${projectId}`),
      ])
      const graphData = await graphRes.json()
      const projectData = await projectRes.json()

      const graph: ProjectGraph = graphData?.data || graphData
      const project = projectData?.project || projectData?.data?.project

      if (project?.title) setProjectTitle(project.title)

      const { nodes: flowNodes, edges: flowEdges } = buildFlowGraph(graph, viewMode)
      setNodes(flowNodes)
      setEdges(flowEdges)
      setStats({
        files: graph.nodes.files.length,
        chunks: graph.nodes.chunks.length,
        imports: graph.edges.imports.length,
        calls: graph.edges.calls.length,
      })
    } catch {
    } finally {
      setLoading(false)
    }
  }, [projectId, viewMode])

  useEffect(() => { loadGraph() }, [loadGraph])

  const onConnect = useCallback(
    (params: Connection) => setEdges(eds => addEdge(params, eds)),
    [setEdges]
  )

  if (loading) {
    return (
      <div className="p-6 space-y-4">
        <Skeleton className="h-6 w-48" />
        <Skeleton className="h-[600px] rounded-xl" />
      </div>
    )
  }

  return (
    <div className="flex flex-col h-[calc(100vh-64px)]">
      {/* Header bar */}
      <div className="flex items-center justify-between px-4 py-2 border-b bg-background shrink-0 gap-4 flex-wrap">
        <div className="flex items-center gap-2 text-sm text-muted-foreground">
          <a href={appPath('/cil')} className="hover:underline">Code Intelligence</a>
          <ChevronRight className="h-3 w-3" />
          <a href={appPath(`/cil/${projectId}/ingest`)} className="hover:underline">{projectTitle || 'Project'}</a>
          <ChevronRight className="h-3 w-3" />
          <span>Graph</span>
        </div>

        <div className="flex items-center gap-2">
          <div className="flex items-center gap-1.5 text-xs text-muted-foreground">
            <span className="font-mono">{stats.files} files</span>
            <span>·</span>
            <span className="font-mono">{stats.chunks} chunks</span>
            <span>·</span>
            <span className="font-mono">{stats.calls} calls</span>
            <span>·</span>
            <span className="font-mono">{stats.imports} imports</span>
          </div>

          <Select value={viewMode} onValueChange={v => setViewMode(v as ViewMode)}>
            <SelectTrigger className="h-7 w-28 text-xs">
              <Layers className="h-3 w-3 mr-1" />
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="chunks">Chunks</SelectItem>
              <SelectItem value="files">Files</SelectItem>
              <SelectItem value="mixed">Mixed</SelectItem>
            </SelectContent>
          </Select>

          <Button size="sm" variant="outline" onClick={loadGraph} className="h-7 text-xs">
            <RefreshCw className="h-3 w-3 mr-1" />
            Refresh
          </Button>
        </div>
      </div>

      {/* Graph canvas + side panel */}
      <div className="flex flex-1 min-h-0">
        <div className="flex-1">
          {nodes.length === 0 ? (
            <div className="flex items-center justify-center h-full text-muted-foreground text-sm">
              <div className="text-center space-y-2">
                <ZoomIn className="h-10 w-10 mx-auto opacity-20" />
                <p>No {viewMode === 'files' ? 'files' : 'chunks'} found.</p>
                <p className="text-xs">Run Pass 1 + Pass 2 ingestion first.</p>
              </div>
            </div>
          ) : (
            <ReactFlow
              nodes={nodes}
              edges={edges}
              onNodesChange={onNodesChange}
              onEdgesChange={onEdgesChange}
              onConnect={onConnect}
              onNodeClick={(_evt, node) => setSelectedNode(node)}
              fitView
              fitViewOptions={{ padding: 0.15 }}
              minZoom={0.1}
              maxZoom={2}
              proOptions={{ hideAttribution: true }}
            >
              <Background color="#E2E8F0" gap={20} size={1} />
              <Controls />
              <MiniMap
                nodeColor={nodeColor}
                style={{ background: '#F8FAFC', border: '1px solid #E2E8F0' }}
                pannable
                zoomable
              />

              {/* Legend */}
              <Panel position="bottom-left">
                <div className="bg-background/90 backdrop-blur border rounded-lg p-2 text-xs space-y-1 shadow-sm">
                  {Object.entries(CHUNK_TYPE_COLORS).map(([type, color]) => (
                    <div key={type} className="flex items-center gap-1.5">
                      <div className="w-3 h-3 rounded-sm shrink-0" style={{ background: color + '44', border: `1.5px solid ${color}` }} />
                      <span className="text-muted-foreground">{type}</span>
                    </div>
                  ))}
                  {viewMode !== 'chunks' && (
                    <div className="flex items-center gap-1.5 pt-1 border-t">
                      <div className="w-3 h-3 rounded-sm shrink-0 bg-slate-900" />
                      <span className="text-muted-foreground">file</span>
                    </div>
                  )}
                </div>
              </Panel>
            </ReactFlow>
          )}
        </div>

        {/* Node detail side panel */}
        <div className="w-56 border-l bg-muted/30 shrink-0 overflow-y-auto">
          <div className="p-2 border-b bg-background">
            <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Inspector</p>
          </div>
          <NodeDetailPanel node={selectedNode} projectId={projectId!} />
        </div>
      </div>
    </div>
  )
}
