/**
 * PageRank 算法实现
 * 基于 Aider 的实现，用于代码符号相关性排序
 */

import { Graph } from './graph.js';

export interface PageRankOptions {
  /** 阻尼系数 (默认 0.85) */
  damping?: number;
  /** 最大迭代次数 (默认 100) */
  iterations?: number;
  /** 收敛阈值 (默认 1e-6) */
  tolerance?: number;
  /** 个性化向量：节点 -> 初始权重 */
  personalization?: Map<string, number>;
}

/**
 * PageRank 算法
 *
 * @param graph - 图结构
 * @param options - 算法选项
 * @returns 节点排名 Map<节点, 排名值>
 */
export function pagerank(
  graph: Graph,
  options: PageRankOptions = {}
): Map<string, number> {
  const {
    damping = 0.85,
    iterations = 100,
    tolerance = 1e-6,
    personalization,
  } = options;

  const nodes = graph.getNodes();
  const n = nodes.length;

  if (n === 0) {
    return new Map();
  }

  // 初始化排名
  let ranks = new Map<string, number>();
  const baseRank = 1 / n;

  // 处理个性化向量
  let persVector = new Map<string, number>();
  if (personalization && personalization.size > 0) {
    // 归一化个性化向量
    const total = Array.from(personalization.values()).reduce((a, b) => a + b, 0);
    if (total > 0) {
      for (const [node, value] of personalization) {
        persVector.set(node, value / total);
      }
    }
  } else {
    // 均匀分布
    for (const node of nodes) {
      persVector.set(node, baseRank);
    }
  }

  // 初始排名 = 个性化向量
  for (const node of nodes) {
    ranks.set(node, persVector.get(node) || baseRank);
  }

  // 迭代计算
  for (let iter = 0; iter < iterations; iter++) {
    const newRanks = new Map<string, number>();
    let diff = 0;

    // 计算悬挂节点的贡献（没有出边的节点）
    let danglingSum = 0;
    for (const node of nodes) {
      const outEdges = graph.getOutEdges(node);
      if (outEdges.length === 0) {
        danglingSum += ranks.get(node) || 0;
      }
    }

    for (const node of nodes) {
      // 基础分数：(1 - damping) * 个性化 + damping * 悬挂贡献
      let rank =
        (1 - damping) * (persVector.get(node) || baseRank) +
        (damping * danglingSum) / n;

      // 收集入边贡献
      const inEdges = graph.getInEdges(node);
      for (const edge of inEdges) {
        const sourceRank = ranks.get(edge.from) || 0;
        const outDegree = graph.getOutDegree(edge.from);

        if (outDegree > 0) {
          // 边权重占源节点总出度的比例
          rank += damping * sourceRank * (edge.weight / outDegree);
        }
      }

      newRanks.set(node, rank);
      diff += Math.abs(rank - (ranks.get(node) || 0));
    }

    ranks = newRanks;

    // 检查收敛
    if (diff < tolerance) {
      break;
    }
  }

  return ranks;
}

/**
 * 将 PageRank 排名分配到定义上
 * 按照 Aider 的方式：将源节点的排名按边权重比例分配给目标定义
 *
 * @param graph - 图结构
 * @param nodeRanks - 节点 PageRank 排名
 * @returns 定义排名 Map<"file:ident", rank>
 */
export function distributeRanksToDefinitions(
  graph: Graph,
  nodeRanks: Map<string, number>
): Map<string, number> {
  const definitionRanks = new Map<string, number>();

  for (const src of graph.getNodes()) {
    const srcRank = nodeRanks.get(src) || 0;
    const outEdges = graph.getOutEdges(src);
    const totalWeight = outEdges.reduce((sum, e) => sum + e.weight, 0);

    if (totalWeight === 0) continue;

    for (const edge of outEdges) {
      const edgeRank = (srcRank * edge.weight) / totalWeight;
      const key = `${edge.to}:${edge.ident}`;
      definitionRanks.set(key, (definitionRanks.get(key) || 0) + edgeRank);
    }
  }

  return definitionRanks;
}
