import React, { useEffect, useMemo, useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Progress } from "@/components/ui/progress"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { BarChart3, Users, Network, GitBranch, TrendingUp, Lightbulb } from "lucide-react"; import { GraphComparisonResults, GraphDetailsResponse } from "@/types"; import { api } from "@/lib/api"; // Prefer system_name when available const getGraphDisplayName = (g?: { filename: string; system_name?: string }) => { if (!g) return ""; return g.system_name && g.system_name.trim().length > 0 ? g.system_name : g.filename; }; interface ComparisonResultsProps { results: GraphComparisonResults; } export const ComparisonResults: React.FC = ({ results, }) => { const graph1_info = results.metadata?.graph1; const graph2_info = results.metadata?.graph2; const [graph1Details, setGraph1Details] = useState(null); const [graph2Details, setGraph2Details] = useState(null); useEffect(() => { const loadDetails = async () => { try { if (graph1_info?.id) setGraph1Details(await api.graphComparison.getGraphDetails(graph1_info.id)); if (graph2_info?.id) setGraph2Details(await api.graphComparison.getGraphDetails(graph2_info.id)); } catch (e) { console.warn("ComparisonResults: failed to load graph details", e); } }; loadDetails(); }, [graph1_info?.id, graph2_info?.id]); const entityExamples = useMemo(() => { const createKey = (e: any) => `${(e?.type || "").toLowerCase()} ${(e?.name || "").toLowerCase()}`.trim(); const toLabel = (e: any) => `${e?.name || "(unnamed)"}${e?.type ? ` (${e.type})` : ""}`; const e1 = graph1Details?.entities || []; const e2 = graph2Details?.entities || []; const set1 = new Map(); const set2 = new Map(); e1.forEach((x) => set1.set(createKey(x), x)); e2.forEach((x) => set2.set(createKey(x), x)); const overlap: string[] = []; const unique1: string[] = []; const unique2: string[] = []; for (const [k, v] of set1) { if (k && set2.has(k)) overlap.push(toLabel(v)); else unique1.push(toLabel(v)); } for (const [k, v] of set2) { if (!k) continue; if (!set1.has(k)) unique2.push(toLabel(v)); } return { overlap: overlap.slice(0, 8), unique1: unique1.slice(0, 8), unique2: unique2.slice(0, 8) }; }, [graph1Details, graph2Details]); const relationExamples = useMemo(() => { const createKey = (r: any) => `${(r?.type || "").toLowerCase()} ${(r?.description || "").toLowerCase()}`.trim(); const toLabel = (r: any) => `${r?.type || "RELATION"}${r?.description ? ` — ${r.description}` : ""}`; const r1 = graph1Details?.relations || []; const r2 = graph2Details?.relations || []; const set1 = new Map(); const set2 = new Map(); r1.forEach((x) => set1.set(createKey(x), x)); r2.forEach((x) => set2.set(createKey(x), x)); const overlap: string[] = []; const unique1: string[] = []; const unique2: string[] = []; for (const [k, v] of set1) { if (k && set2.has(k)) overlap.push(toLabel(v)); else unique1.push(toLabel(v)); } for (const [k, v] of set2) { if (!k) continue; if (!set1.has(k)) unique2.push(toLabel(v)); } return { overlap: overlap.slice(0, 8), unique1: unique1.slice(0, 8), unique2: unique2.slice(0, 8) }; }, [graph1Details, graph2Details]); // Get entity and relation counts from the actual metrics const graph1_entity_count = results.entity_metrics.unique_to_graph1 + results.entity_metrics.overlap_count; const graph2_entity_count = results.entity_metrics.unique_to_graph2 + results.entity_metrics.overlap_count; const graph1_relation_count = results.relation_metrics.unique_to_graph1 + results.relation_metrics.overlap_count; const graph2_relation_count = results.relation_metrics.unique_to_graph2 + results.relation_metrics.overlap_count; const formatPercentage = (value: number | undefined | null) => { if (value === undefined || value === null || isNaN(value)) { return "N/A"; } return `${(value * 100).toFixed(1)}%`; }; const formatNumber = ( value: number | undefined | null, decimals: number = 3 ) => { if (value === undefined || value === null || isNaN(value)) { return "N/A"; } return value.toFixed(decimals); }; const safeValue = ( value: number | undefined | null, defaultValue: number = 0 ) => { if (value === undefined || value === null || isNaN(value)) { return defaultValue; } return value; }; const getScoreColor = (score: number | undefined | null) => { const safeScore = safeValue(score); if (safeScore >= 0.8) return "text-green-600"; if (safeScore >= 0.6) return "text-yellow-600"; return "text-red-600"; }; const getScoreVariant = (score: number | undefined | null) => { const safeScore = safeValue(score); if (safeScore >= 0.8) return "default"; if (safeScore >= 0.6) return "secondary"; return "destructive"; }; // Handle case where metadata is missing if (!graph1_info || !graph2_info) { return (

Graph information not available

); } return (
{/* Key Insights */} Key Insights
  • Entities overlap {formatPercentage(results.entity_metrics.overlap_ratio)} • shared {results.entity_metrics.overlap_count}; unique G1 {results.entity_metrics.unique_to_graph1}, G2 {results.entity_metrics.unique_to_graph2}.
  • Relations overlap {formatPercentage(results.relation_metrics.overlap_ratio)} • shared {results.relation_metrics.overlap_count}; unique G1 {results.relation_metrics.unique_to_graph1}, G2 {results.relation_metrics.unique_to_graph2}.
  • Structural density difference {formatNumber(Math.abs(results.structural_metrics.density_difference), 3)} • common patterns {results.structural_metrics.common_patterns_count}.
{/* Header Summary */} Comparison Overview
{/* Graph 1 Info */}

Graph 1

{getGraphDisplayName(graph1_info)}

{graph1_entity_count} entities • {graph1_relation_count}{" "} relations
{/* Graph 2 Info */}

Graph 2

{getGraphDisplayName(graph2_info)}

{graph2_entity_count} entities • {graph2_relation_count}{" "} relations
{/* Overall Similarity Scores */}
{formatPercentage( results.overall_metrics.structural_similarity )}
Structural
{formatPercentage(results.overall_metrics.content_similarity)}
Content
{formatPercentage(results.overall_metrics.overall_similarity)}
Overall
{/* Detailed Metrics */} Detailed Analysis {/* Entity Metrics */}
Entity Analysis {formatPercentage( results.entity_metrics.semantic_similarity )}
{results.entity_metrics.overlap_count}
Overlapping
{results.entity_metrics.unique_to_graph1}
Unique to Graph 1
{results.entity_metrics.unique_to_graph2}
Unique to Graph 2
{formatPercentage(results.entity_metrics.overlap_ratio)}
Overlap Ratio
Semantic Similarity {formatPercentage( results.entity_metrics.semantic_similarity )}
{/* Examples */} {(entityExamples.overlap.length || entityExamples.unique1.length || entityExamples.unique2.length) ? (
Common examples
{entityExamples.overlap.map((e, i) => ( {e} ))} {!entityExamples.overlap.length && None}
Unique to Graph 1
{entityExamples.unique1.map((e, i) => ( {e} ))} {!entityExamples.unique1.length && None}
Unique to Graph 2
{entityExamples.unique2.map((e, i) => ( {e} ))} {!entityExamples.unique2.length && None}
) : null}
{/* Relation Metrics */}
Relation Analysis {formatPercentage( results.relation_metrics.semantic_similarity )}
{results.relation_metrics.overlap_count}
Overlapping
{results.relation_metrics.unique_to_graph1}
Unique to Graph 1
{results.relation_metrics.unique_to_graph2}
Unique to Graph 2
{formatPercentage( results.relation_metrics.overlap_ratio )}
Overlap Ratio
Semantic Similarity {formatPercentage( results.relation_metrics.semantic_similarity )}
{/* Examples */} {(relationExamples.overlap.length || relationExamples.unique1.length || relationExamples.unique2.length) ? (
Common examples
{relationExamples.overlap.map((e, i) => ( {e} ))} {!relationExamples.overlap.length && None}
Unique to Graph 1
{relationExamples.unique1.map((e, i) => ( {e} ))} {!relationExamples.unique1.length && None}
Unique to Graph 2
{relationExamples.unique2.map((e, i) => ( {e} ))} {!relationExamples.unique2.length && None}
) : null}
{/* Structural Metrics */}
Structural Analysis {formatPercentage( results.overall_metrics.structural_similarity )}
Graph 1 Density
{formatNumber( results.structural_metrics.graph1_density, 3 )}
Graph 2 Density
{formatNumber( results.structural_metrics.graph2_density, 3 )}
Density Difference
{formatNumber( Math.abs( safeValue( results.structural_metrics.density_difference ) ), 3 )}
Common Patterns
{results.structural_metrics.common_patterns_count}
{/* Type Distribution */}
Type Distribution
Entity Type Similarity {formatPercentage( results.type_distribution_metrics .entity_type_similarity )}
Relation Type Similarity {formatPercentage( results.type_distribution_metrics .relation_type_similarity )}
{/* Comparison Metadata */} Comparison Details
Comparison completed at:{" "} {results.metadata?.comparison_timestamp ? new Date(results.metadata.comparison_timestamp).toLocaleString() : "Unknown"}
); };