"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import type { Node, Edge } from "@xyflow/react";
import {
  ReactFlow,
  Background,
  Controls,
  MiniMap,
  Panel,
  useNodesState,
  useEdgesState,
  MarkerType,
  BackgroundVariant,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { fetchKnowledgeGraph } from "@/lib/api/knowledge";
import { MosaicSpinner } from "@/components/ui/MosaicSpinner";
import ELK from "elkjs/lib/elk.bundled.js";

// PDA-friendly status colors from CLAUDE.md
const STATUS_COLORS = {
  PUBLISHED: "#10B981", // green-500 - Active
  DRAFT: "#3B82F6", // blue-500 - Scheduled/Upcoming
  ARCHIVED: "#9CA3AF", // gray-400 - Dormant
} as const;

const ORPHAN_COLOR = "#D1D5DB"; // gray-300 - Orphaned nodes

type LayoutType = "force" | "hierarchical" | "circular";

interface GraphNode {
  id: string;
  slug: string;
  title: string;
  summary: string | null;
  status?: string;
  tags: {
    id: string;
    name: string;
    slug: string;
    color: string | null;
  }[];
  depth: number;
  isOrphan?: boolean;
}

interface GraphEdge {
  id: string;
  sourceId: string;
  targetId: string;
  linkText: string;
}

interface GraphData {
  nodes: GraphNode[];
  edges: GraphEdge[];
  stats: {
    totalNodes: number;
    totalEdges: number;
    orphanCount: number;
  };
}

interface KnowledgeGraphViewerProps {
  initialFilters?: {
    tags?: string[];
    status?: string;
    limit?: number;
  };
}

const elk = new ELK();

/**
 * Calculate node size based on number of connections
 */
function calculateNodeSize(connectionCount: number): number {
  const minSize = 40;
  const maxSize = 120;
  const size = minSize + Math.min(connectionCount * 8, maxSize - minSize);
  return size;
}

/**
 * Get node color based on status (PDA-friendly)
 */
function getNodeColor(node: GraphNode): string {
  if (node.isOrphan) {
    return ORPHAN_COLOR;
  }
  const status = node.status as keyof typeof STATUS_COLORS;
  return status in STATUS_COLORS ? STATUS_COLORS[status] : STATUS_COLORS.PUBLISHED;
}

/**
 * Calculate connection count for each node
 */
function calculateConnectionCounts(nodes: GraphNode[], edges: GraphEdge[]): Map<string, number> {
  const counts = new Map<string, number>();

  // Initialize all nodes with 0
  nodes.forEach((node) => counts.set(node.id, 0));

  // Count connections
  edges.forEach((edge) => {
    counts.set(edge.sourceId, (counts.get(edge.sourceId) ?? 0) + 1);
    counts.set(edge.targetId, (counts.get(edge.targetId) ?? 0) + 1);
  });

  return counts;
}

/**
 * Convert graph data to ReactFlow nodes
 */
function convertToReactFlowNodes(
  nodes: GraphNode[],
  edges: GraphEdge[],
  layout: LayoutType
): Node[] {
  const connectionCounts = calculateConnectionCounts(nodes, edges);

  return nodes.map((node, index) => {
    const connectionCount = connectionCounts.get(node.id) ?? 0;
    const size = calculateNodeSize(connectionCount);
    const color = getNodeColor(node);

    return {
      id: node.id,
      type: "default",
      position: getInitialPosition(index, nodes.length, layout),
      data: {
        label: node.title,
        slug: node.slug,
        summary: node.summary,
        status: node.status,
        tags: node.tags,
        connectionCount,
        isOrphan: node.isOrphan,
      },
      style: {
        width: size,
        height: size,
        backgroundColor: color,
        color: "#FFFFFF",
        border: "2px solid #FFFFFF",
        borderRadius: "50%",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        padding: "8px",
        fontSize: Math.max(10, size / 8),
        fontWeight: 600,
        textAlign: "center",
        cursor: "pointer",
        boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
      },
    };
  });
}

/**
 * Convert graph data to ReactFlow edges
 */
function convertToReactFlowEdges(edges: GraphEdge[]): Edge[] {
  return edges.map((edge) => ({
    id: edge.id,
    source: edge.sourceId,
    target: edge.targetId,
    type: "smoothstep",
    animated: false,
    markerEnd: {
      type: MarkerType.ArrowClosed,
      width: 20,
      height: 20,
    },
    style: {
      strokeWidth: 2,
      stroke: "#94A3B8", // slate-400
    },
    label: edge.linkText,
    labelStyle: {
      fontSize: 10,
      fill: "#64748B", // slate-500
    },
  }));
}

/**
 * Get initial position based on layout type
 */
function getInitialPosition(
  index: number,
  totalNodes: number,
  layout: LayoutType
): { x: number; y: number } {
  switch (layout) {
    case "circular": {
      const radius = Math.max(300, totalNodes * 20);
      const angle = (index / totalNodes) * 2 * Math.PI;
      return {
        x: 400 + radius * Math.cos(angle),
        y: 400 + radius * Math.sin(angle),
      };
    }
    case "hierarchical": {
      const cols = Math.ceil(Math.sqrt(totalNodes));
      return {
        x: (index % cols) * 250,
        y: Math.floor(index / cols) * 200,
      };
    }
    case "force":
    default:
      // Random initial positions for force layout
      return {
        x: Math.random() * 800,
        y: Math.random() * 600,
      };
  }
}

/**
 * Apply ELK hierarchical layout
 */
async function applyElkLayout(nodes: Node[], edges: Edge[]): Promise<Node[]> {
  const graph = {
    id: "root",
    layoutOptions: {
      "elk.algorithm": "layered",
      "elk.direction": "DOWN",
      "elk.spacing.nodeNode": "80",
      "elk.layered.spacing.nodeNodeBetweenLayers": "100",
    },
    children: nodes.map((node) => ({
      id: node.id,
      width: typeof node.style?.width === "number" ? node.style.width : 100,
      height: typeof node.style?.height === "number" ? node.style.height : 100,
    })),
    edges: edges.map((edge) => ({
      id: edge.id,
      sources: [edge.source],
      targets: [edge.target],
    })),
  };

  const layout = await elk.layout(graph);

  return nodes.map((node) => {
    const elkNode = layout.children?.find((n) => n.id === node.id);
    if (elkNode?.x !== undefined && elkNode.y !== undefined) {
      return {
        ...node,
        position: { x: elkNode.x, y: elkNode.y },
      };
    }
    return node;
  });
}

export function KnowledgeGraphViewer({
  initialFilters,
}: KnowledgeGraphViewerProps): React.JSX.Element {
  const router = useRouter();
  const [graphData, setGraphData] = useState<GraphData | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [layout, setLayout] = useState<LayoutType>("force");
  const [showOrphans, setShowOrphans] = useState(true);
  const [statusFilter, setStatusFilter] = useState<string>(initialFilters?.status ?? "");
  const [tagFilter, setTagFilter] = useState<string>("");

  // Load graph data
  const loadGraph = useCallback(async (): Promise<void> => {
    try {
      setIsLoading(true);
      setError(null);
      const data = await fetchKnowledgeGraph(initialFilters);
      setGraphData(data);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to load graph");
    } finally {
      setIsLoading(false);
    }
  }, [initialFilters]);

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

  // Filter nodes based on criteria
  const filteredNodes = useMemo(() => {
    if (!graphData) return [];

    let filtered = graphData.nodes;

    // Filter by orphan status
    if (!showOrphans) {
      filtered = filtered.filter((node) => !node.isOrphan);
    }

    // Filter by status
    if (statusFilter) {
      filtered = filtered.filter((node) => node.status === statusFilter);
    }

    // Filter by tag
    if (tagFilter) {
      const lowerFilter = tagFilter.toLowerCase();
      filtered = filtered.filter((node) =>
        node.tags.some((tag) => tag.name.toLowerCase().includes(lowerFilter))
      );
    }

    return filtered;
  }, [graphData, showOrphans, statusFilter, tagFilter]);

  // Filter edges to only include those between visible nodes
  const filteredEdges = useMemo(() => {
    if (!graphData) return [];

    const nodeIds = new Set(filteredNodes.map((n) => n.id));
    return graphData.edges.filter(
      (edge) => nodeIds.has(edge.sourceId) && nodeIds.has(edge.targetId)
    );
  }, [graphData, filteredNodes]);

  // Convert to ReactFlow format
  const initialNodes = useMemo(
    () => convertToReactFlowNodes(filteredNodes, filteredEdges, layout),
    [filteredNodes, filteredEdges, layout]
  );

  const initialEdges = useMemo(() => convertToReactFlowEdges(filteredEdges), [filteredEdges]);

  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

  // Update nodes when layout or data changes
  useEffect(() => {
    const updateLayout = async (): Promise<void> => {
      let newNodes = convertToReactFlowNodes(filteredNodes, filteredEdges, layout);

      if (layout === "hierarchical") {
        newNodes = await applyElkLayout(newNodes, initialEdges);
      }

      setNodes(newNodes);
    };

    void updateLayout();
  }, [filteredNodes, filteredEdges, layout, initialEdges, setNodes]);

  // Update edges when data changes
  useEffect(() => {
    setEdges(initialEdges);
  }, [initialEdges, setEdges]);

  // Handle node click - navigate to entry
  const handleNodeClick = useCallback(
    (_event: React.MouseEvent, node: Node): void => {
      const slug = node.data.slug as string;
      if (slug) {
        router.push(`/knowledge/${slug}`);
      }
    },
    [router]
  );

  // Handle layout change
  const handleLayoutChange = useCallback((newLayout: LayoutType): void => {
    setLayout(newLayout);
  }, []);

  if (isLoading) {
    return (
      <div className="flex items-center justify-center h-screen">
        <MosaicSpinner size={48} label="Loading knowledge graph..." />
      </div>
    );
  }

  if (error || !graphData) {
    return (
      <div className="flex flex-col items-center justify-center h-screen p-8">
        <div className="text-xl font-semibold mb-2" style={{ color: "var(--danger)" }}>
          Error Loading Graph
        </div>
        <div className="text-sm text-gray-500 dark:text-gray-400">{error}</div>
        <button
          onClick={loadGraph}
          className="mt-4 px-4 py-2 rounded text-white"
          style={{ background: "var(--danger)" }}
        >
          Retry
        </button>
      </div>
    );
  }

  return (
    <div className="h-screen flex flex-col bg-white dark:bg-gray-900">
      {/* Header */}
      <div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
        <div className="flex items-center gap-4">
          <h2 className="text-xl font-bold text-gray-900 dark:text-gray-100">Knowledge Graph</h2>
          <div className="text-sm text-gray-500 dark:text-gray-400">
            {filteredNodes.length} entries • {filteredEdges.length} connections
            {graphData.stats.orphanCount > 0 && (
              <span className="ml-2">• {graphData.stats.orphanCount} orphaned</span>
            )}
          </div>
        </div>

        {/* Layout Controls */}
        <div className="flex items-center gap-3">
          <label className="text-sm font-medium text-gray-700 dark:text-gray-300">Layout:</label>
          <div className="flex rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700">
            {(["force", "hierarchical", "circular"] as const).map((layoutType) => (
              <button
                key={layoutType}
                onClick={() => {
                  handleLayoutChange(layoutType);
                }}
                className={`px-3 py-1.5 text-sm font-medium transition-colors capitalize ${
                  layout === layoutType
                    ? "bg-blue-500 text-white"
                    : "bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
                }`}
              >
                {layoutType}
              </button>
            ))}
          </div>
        </div>
      </div>

      {/* Filters */}
      <div className="flex items-center gap-4 p-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800">
        {/* Status Filter */}
        <div className="flex items-center gap-2">
          <label
            htmlFor="status-filter"
            className="text-sm font-medium text-gray-700 dark:text-gray-300"
          >
            Status:
          </label>
          <select
            id="status-filter"
            value={statusFilter}
            onChange={(e) => {
              setStatusFilter(e.target.value);
            }}
            className="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
          >
            <option value="">All</option>
            <option value="PUBLISHED">Published</option>
            <option value="DRAFT">Draft</option>
            <option value="ARCHIVED">Archived</option>
          </select>
        </div>

        {/* Tag Filter */}
        <div className="flex items-center gap-2">
          <label
            htmlFor="tag-filter"
            className="text-sm font-medium text-gray-700 dark:text-gray-300"
          >
            Tags:
          </label>
          <input
            id="tag-filter"
            type="text"
            value={tagFilter}
            onChange={(e) => {
              setTagFilter(e.target.value);
            }}
            placeholder="Filter by tags..."
            className="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
          />
        </div>

        {/* Orphan Toggle */}
        <div className="flex items-center gap-2 ml-auto">
          <label
            htmlFor="orphan-toggle"
            className="text-sm font-medium text-gray-700 dark:text-gray-300"
          >
            Show Orphans:
          </label>
          <button
            id="orphan-toggle"
            role="switch"
            aria-checked={showOrphans}
            onClick={() => {
              setShowOrphans(!showOrphans);
            }}
            className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
              showOrphans ? "bg-blue-500" : "bg-gray-300 dark:bg-gray-600"
            }`}
          >
            <span
              className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
                showOrphans ? "translate-x-6" : "translate-x-1"
              }`}
            />
          </button>
        </div>
      </div>

      {/* Graph Visualization */}
      <div className="flex-1">
        <ReactFlow
          nodes={nodes}
          edges={edges}
          onNodesChange={onNodesChange}
          onEdgesChange={onEdgesChange}
          onNodeClick={handleNodeClick}
          fitView
          minZoom={0.1}
          maxZoom={2}
          defaultViewport={{ x: 0, y: 0, zoom: 0.8 }}
        >
          <Background variant={BackgroundVariant.Dots} gap={12} size={1} />
          <Controls />
          <MiniMap
            nodeColor={(node) => {
              const bgColor = node.style?.backgroundColor;
              return typeof bgColor === "string" ? bgColor : "#3B82F6";
            }}
            maskColor="rgba(0, 0, 0, 0.1)"
          />
          <Panel
            position="bottom-left"
            className="bg-white dark:bg-gray-800 p-3 rounded-lg shadow-lg"
          >
            <div className="text-xs text-gray-600 dark:text-gray-400 space-y-1">
              <div className="flex items-center gap-2">
                <div
                  className="w-3 h-3 rounded-full"
                  style={{ backgroundColor: STATUS_COLORS.PUBLISHED }}
                />
                <span>Published (Active)</span>
              </div>
              <div className="flex items-center gap-2">
                <div
                  className="w-3 h-3 rounded-full"
                  style={{ backgroundColor: STATUS_COLORS.DRAFT }}
                />
                <span>Draft (Upcoming)</span>
              </div>
              <div className="flex items-center gap-2">
                <div
                  className="w-3 h-3 rounded-full"
                  style={{ backgroundColor: STATUS_COLORS.ARCHIVED }}
                />
                <span>Archived (Dormant)</span>
              </div>
              <div className="flex items-center gap-2">
                <div className="w-3 h-3 rounded-full" style={{ backgroundColor: ORPHAN_COLOR }} />
                <span>Orphaned</span>
              </div>
            </div>
          </Panel>
        </ReactFlow>
      </div>
    </div>
  );
}
