const fs = require('fs');
const path = require('path');
const GraphStore = require('./graph.js');
const NamespaceRegistry = require('./namespace.js');

/**
 * Phase 7F: Supergraph — Multi-Repo Merge
 * Merges multiple repo graph snapshots into a unified super-graph,
 * resolving cross-repo references via the namespace registry.
 */

/**
 * Merge multiple repo snapshots into a single super-graph.
 * Prefixes all node IDs and edge source/targets with repoId to avoid collisions.
 * Resolves cross-repo CALLS/IMPORTS via namespace registry.
 *
 * @param {Array<{repoId: string, snapshotPath: string}>} repos
 * @param {object} opts - { overridesPath }
 * @returns {{ graph: GraphStore, registry: NamespaceRegistry, crossRepoEdges: Array, stats: object }}
 */
function buildSupergraph(repos, opts = {}) {
  const snapshots = [];
  const graphs = [];

  // Load all snapshots
  for (const repo of repos) {
    const graph = GraphStore.loadSnapshot(repo.snapshotPath);
    snapshots.push({ repoId: repo.repoId, snapshot: graph });
    graphs.push({ repoId: repo.repoId, graph });
  }

  // Build namespace registry
  const registry = NamespaceRegistry.build(snapshots);
  if (opts.overridesPath) {
    registry.loadOverrides(opts.overridesPath);
  }

  // Create merged graph
  const merged = new GraphStore();
  const crossRepoEdges = [];
  const stats = {
    repos: repos.length,
    totalNodes: 0,
    totalEdges: 0,
    crossRepoEdges: 0,
    resolvedCalls: 0,
    unresolvedCalls: 0,
  };

  // 1. Merge all nodes with repo-prefixed IDs
  for (const { repoId, graph } of graphs) {
    for (const [id, node] of graph.nodes) {
      const prefixedId = `${repoId}::${id}`;
      merged.nodes.set(prefixedId, {
        ...node,
        _repo: repoId,
        _originalId: id,
      });
      stats.totalNodes++;
    }

    // Merge file index
    for (const [file, ids] of graph.fileIndex) {
      const prefixedFile = `${repoId}::${file}`;
      const prefixedIds = (Array.isArray(ids) ? ids : [...ids]).map(id => `${repoId}::${id}`);
      merged.fileIndex.set(prefixedFile, prefixedIds);
    }
  }

  // 2. Merge all edges, resolving cross-repo references
  for (const { repoId, graph } of graphs) {
    for (const edge of graph.edges) {
      const prefixedSource = `${repoId}::${edge.source}`;

      // Check if target is internal
      if (graph.nodes.has(edge.target) || edge.target.startsWith('dep:')) {
        const prefixedTarget = edge.target.startsWith('dep:')
          ? `${repoId}::${edge.target}`
          : `${repoId}::${edge.target}`;

        merged.edges.push({
          type: edge.type,
          source: prefixedSource,
          target: prefixedTarget,
          _repo: repoId,
        });
        stats.totalEdges++;
        continue;
      }

      // Try to resolve via namespace registry
      if (edge.type === 'CALLS' || edge.type === 'IMPORTS') {
        const resolution = registry.resolve(edge.target, repoId);
        if (resolution) {
          const resolvedTarget = `${resolution.resolvedTo.repoId}::${resolution.resolvedTo.entityId}`;
          const crossEdge = {
            type: edge.type,
            source: prefixedSource,
            target: resolvedTarget,
            _repo: repoId,
            _crossRepo: true,
            _tier: resolution.tier,
            _confidence: resolution.confidence,
            _targetRepo: resolution.resolvedTo.repoId,
          };
          merged.edges.push(crossEdge);
          crossRepoEdges.push(crossEdge);
          stats.totalEdges++;
          stats.crossRepoEdges++;
          stats.resolvedCalls++;
        } else {
          // Unresolved — keep as-is with repo prefix
          merged.edges.push({
            type: edge.type,
            source: prefixedSource,
            target: `${repoId}::${edge.target}`,
            _repo: repoId,
            _unresolved: true,
          });
          stats.totalEdges++;
          stats.unresolvedCalls++;
        }
      } else {
        // Non-CALLS/IMPORTS edges (CONTAINS, IMPLEMENTS)
        merged.edges.push({
          type: edge.type,
          source: prefixedSource,
          target: `${repoId}::${edge.target}`,
          _repo: repoId,
        });
        stats.totalEdges++;
      }
    }
  }

  return { graph: merged, registry, crossRepoEdges, stats };
}

/**
 * Save the supergraph snapshot to disk.
 */
function saveSupergraph(result, outputDir) {
  fs.mkdirSync(outputDir, { recursive: true });

  // Save merged graph
  const graphPath = path.join(outputDir, 'supergraph.json');
  const graphData = {
    nodes: Object.fromEntries(result.graph.nodes),
    edges: result.graph.edges,
    fileIndex: Object.fromEntries(result.graph.fileIndex),
  };
  fs.writeFileSync(graphPath, JSON.stringify(graphData, null, 2));

  // Save registry
  const regPath = path.join(outputDir, 'registry.json');
  fs.writeFileSync(regPath, JSON.stringify(result.registry.toJSON(), null, 2));

  // Save cross-repo edges summary
  const xrepoPath = path.join(outputDir, 'cross-repo-edges.json');
  fs.writeFileSync(xrepoPath, JSON.stringify(result.crossRepoEdges, null, 2));

  // Save stats
  const statsPath = path.join(outputDir, 'stats.json');
  fs.writeFileSync(statsPath, JSON.stringify(result.stats, null, 2));

  return { graphPath, regPath, xrepoPath, statsPath };
}

if (require.main === module) {
  const args = process.argv.slice(2);
  if (args.length < 2) {
    console.error('Usage: node supergraph.js <outputDir> <repoId1:snapshot1.json> [repoId2:snapshot2.json ...]');
    process.exit(1);
  }

  const outputDir = args[0];
  const repos = args.slice(1).map(arg => {
    const [repoId, snapshotPath] = arg.split(':');
    return { repoId, snapshotPath };
  });

  console.log(`Merging ${repos.length} repos...`);
  const result = buildSupergraph(repos);
  const paths = saveSupergraph(result, outputDir);

  console.log(`Supergraph built:`);
  console.log(`  Repos: ${result.stats.repos}`);
  console.log(`  Nodes: ${result.stats.totalNodes}`);
  console.log(`  Edges: ${result.stats.totalEdges}`);
  console.log(`  Cross-repo edges: ${result.stats.crossRepoEdges}`);
  console.log(`  Resolved: ${result.stats.resolvedCalls}, Unresolved: ${result.stats.unresolvedCalls}`);
  console.log(`  Saved to: ${outputDir}`);
}

module.exports = { buildSupergraph, saveSupergraph };
