export class RelationSnapshotAccess {
  constructor(manager) {
    this.manager = manager;
  }

  getDirectRelation(srcId, relation, dstId) {
    const graph = this.manager.arbiter.snapshotGraph;
    const relId = graph.getExistingRelationId(relation);
    if (relId === undefined) return null;
    const edgeIdx = graph.findEdge(srcId, relId, dstId);
    if (edgeIdx === null) return null;
    return this._getSnapshotRelation(edgeIdx, relId);
  }

  getRelationsFromSrc(srcId, relation) {
    const graph = this.manager.arbiter.snapshotGraph;
    const relId = graph.getExistingRelationId(relation);
    if (relId === undefined) return [];
    const wavelet = graph._waveletByRelId ? graph._waveletByRelId[relId] : null;
    if (!wavelet) return [];
    const edges = wavelet.getEdgesForSource(srcId);
    if (!edges.length) return [];
    const results = [];
    for (let i = 0; i < edges.length; i++) {
      const edgeIdx = edges[i];
      // wavelet is relation-specific, no rel filter needed
      results.push(this._getSnapshotRelation(edgeIdx, relId));
    }
    return results;
  }

  getRelationsToDst(dstId, relation) {
    const graph = this.manager.arbiter.snapshotGraph;
    const relId = graph.getExistingRelationId(relation);
    if (relId === undefined) return [];
    const results = [];
    for (let edgeIdx = 0; edgeIdx < graph.edgeIndex; edgeIdx++) {
      const edgeDstId = graph.adjacency
        ? graph.adjacency[edgeIdx * 6 + 2]
        : graph.edgeDstIds[edgeIdx];
      if (edgeDstId !== dstId) continue;
      const edgeRelId = graph.adjacency
        ? graph.adjacency[edgeIdx * 6 + 1]
        : graph.edgeRelIds[edgeIdx];
      if (edgeRelId !== relId) continue;
      results.push(this._getSnapshotRelation(edgeIdx, relId));
    }
    return results;
  }

  getRelationsByName(relation) {
    const graph = this.manager.arbiter.snapshotGraph;
    const relId = graph.getExistingRelationId(relation);
    if (relId === undefined) return [];
    const results = [];
    for (let edgeIdx = 0; edgeIdx < graph.edgeIndex; edgeIdx++) {
      const edgeRelId = graph.adjacency
        ? graph.adjacency[edgeIdx * 6 + 1]
        : graph.edgeRelIds[edgeIdx];
      if (edgeRelId !== relId) continue;
      results.push(this._getSnapshotRelation(edgeIdx, relId));
    }
    return results;
  }

  _getSnapshotRelation(edgeIdx, relId) {
    const graph = this.manager.arbiter.snapshotGraph;
    const srcId = graph.adjacency
      ? graph.adjacency[edgeIdx * 6 + 0]
      : graph.edgeSrcIds[edgeIdx];
    const dstId = graph.adjacency
      ? graph.adjacency[edgeIdx * 6 + 2]
      : graph.edgeDstIds[edgeIdx];
    const value = graph.values[edgeIdx];
    const relationName = graph.getRelationName(relId);
    const possBits = graph.adjacency
      ? graph.adjacency[edgeIdx * 6 + 3]
      : graph.edgePossibilityBits[edgeIdx];
    const relBits = graph.adjacency
      ? graph.adjacency[edgeIdx * 6 + 4]
      : graph.edgeReliabilityBits[edgeIdx];
    const result = {
      src: srcId,
      rel: relationName,
      relId,
      dst: dstId,
      possibility: graph._bitsToFloat(possBits),
      reliability: graph._bitsToFloat(relBits),
      value: Number.isNaN(value) ? undefined : value,
      updated_last_at: graph._builtAt || Date.now(),
      changed_last_at: graph._builtAt || Date.now(),
      source: 'snapshot'
    };
    // Merge per-relation metadata the edge channel does not carry (validity
    // labels, decay configs) so persistence is lossless.
    const metaKey = `${srcId}|${relationName}|${dstId}`;
    const meta = this.manager.arbiter._snapshotRelationMetadata
      ? this.manager.arbiter._snapshotRelationMetadata[metaKey]
      : null;
    if (meta) {
      if (meta.validity !== undefined) result.validity = meta.validity;
      if (meta.decayConfig !== undefined) result.decayConfig = meta.decayConfig;
    }
    return result;
  }
}
