/**
 * Succinct Graph - Working Implementation
 * 
 * Simplified from graph-core but tested and ready for integration
 * 
 * Main changes from graph-core version:
 * - Removed PLTC, write-log, complex adaptive rebuild
 * - Added isAdjacent() method for authorization queries
 */

// Simplified implementation for tests

export class SuccinctGraph {
  constructor(data = null) {
    // Graph metadata
    this.n = data?.n || 0;
    this.M = data?.M || 0;
    this.isLabelled = data?.isLabelled || false;
    
    // Vertex information
    this.vertices = data?.vertices || [];
    this._edges = data?._edges || [];
    this._relationIdToId = data?._relationIdToId || new Map();
  }

  /**
   * Add a node to the graph
   * @param {string} key - Node identifier
   * @returns {number} Node ID
   */
  addNode(key) {
    const nodeId = this.vertices.length;
    this.vertices.push(key);
    this.n = this.vertices.length;
    return nodeId;
  }

  /**
   * Get node ID by key
   * @param {string} key - Node identifier
   * @returns {number|undefined} Node ID or undefined
   */
  getNodeId(key) {
    return this.vertices.indexOf(key);
  }

  /**
   * Get all node keys
   * @returns {Array<string>} Array of node keys
   */
  getNodes() {
    return [...this.vertices];
  }

  /**
   * Add a directed edge
   * @param {string|number} src - Source node key or ID
   * @param {string} relation - Relation name
   * @param {string|number} dst - Destination node key or ID
   * @param {Object} options - {possibility, reliability}
   * @returns {number} Edge index
   */
  addEdge(src, relation, dst, options = {}) {
    const srcId = typeof src === 'number' ? src : this.getNodeId(src);
    const dstId = typeof dst === 'number' ? dst : this.getNodeId(dst);
    const relId = this.getRelationId(relation);
    
    // Store edge in adjacency (using simple array-based approach)
    const edgeIndex = this._edges.length;
    this._edges.push({ srcId, relId, dstId, ...options });
    this.M = this._edges.length;
    
    return edgeIndex;
  }

  getRelationId(relation) {
    if (!this._relationIdToId.has(relation)) {
      const id = this._relationIdToId.size;
      this._relationIdToId.set(relation, id);
      return id;
    }
    return this._relationIdToId.get(relation) || 0;
  }

  addOutEdge(src, relation, dst, options = {}) {
    if (dst === undefined) {
      return this.addEdge(src, null, relation, options);
    }
    return this.addEdge(src, relation, dst, options);
  }

  _getEdge(index) {
    if (index < 0 || index >= this._edges.length) {
      return null;
    }
    return this._edges[index];
  }

  getEdge(index) {
    const edge = this._getEdge(index);
    if (!edge) return null;
    return {
      src: this.vertices[edge.srcId],
      dst: this.vertices[edge.dstId],
      relId: edge.relId
    };
  }

  /**
   * Check if edge exists
   * @param {string|number} src - Source node
   * @param {string} relation - Relation name  
   * @param {string|number} dst - Destination node
   * @returns {boolean} True if edge exists
   */
  hasEdge(src, relation, dst) {
    return this.findEdge(src, this.getRelationId(relation), dst) !== null;
  }

  findEdge(src, relId, dst) {
    const srcId = typeof src === 'number' ? src : this.getNodeId(src);
    const dstId = typeof dst === 'number' ? dst : this.getNodeId(dst);
    for (let i = 0; i < this._edges.length; i++) {
      const edge = this._edges[i];
      if (edge.srcId === srcId && edge.relId === relId && edge.dstId === dstId) {
        return i;
      }
    }
    return null;
  }

  /**
   * Find edge by criteria
   * @param {string|number|null} src - Source node filter
   * @param {string|null} relation - Relation name filter
   * @param {string|null} dst - Destination node filter
   * @returns {Array} Array of matching edge indices
   */
  findEdges(src = null, relation = null, dst = null) {
    const edges = [];
    
    for (let i = 0; i < this._edges.length; i++) {
      const edge = this.getEdge(i);
      const srcMatch = src === null || edge.src === src;
      const relMatch = relation === null || edge.relId === this.getRelationId(relation);
      const dstMatch = dst === null || edge.dst === dst;
      
      if (srcMatch && relMatch && dstMatch) {
        edges.push(i);
      }
    }
    
    return edges;
  }

  /**
   * Get all outgoing edges from a node
   * @param {string|number} src - Source node
   * @returns {Array<number>} Array of edge indices
   */
  getOutEdges(src) {
    const edges = [];
    const srcId = typeof src === 'number' ? src : this.getNodeId(src);
    for (let i = 0; i < this._edges.length; i++) {
      const edge = this._edges[i];
      if (edge.srcId === srcId) {
        edges.push(i);
      }
    }
    return edges;
  }

  forEachOutEdge(src, callback) {
    const edges = this.getOutEdges(src);
    for (let i = 0; i < edges.length; i++) {
      const edgeIndex = edges[i];
      callback(this.getEdge(edgeIndex), edgeIndex);
    }
  }

  /**
   * Get vertex information
   */
  getVertex(key) {
    const nodeId = this.getNodeId(key);
    if (nodeId === undefined) {
      return null;
    }
    
    return { key, nodeId };
  }

  /**
   * Check adjacency (used by authorization checker)
   */
  isAdjacent(u, v) {
    const uId = typeof u === 'number' ? u : this.getNodeId(u);
    const vId = typeof v === 'number' ? v : this.getNodeId(v);
    
    for (let i = 0; i < this._edges.length; i++) {
      const edge = this._edges[i];
      if (edge.srcId === uId && edge.dstId === vId) {
        return true;
      }
    }
    return false;
  }

  numEdges() {
    return this._edges.length;
  }

  getStats() {
    return {
      n: this.n,
      numEdges: this._edges.length,
      numNodes: this.vertices.length,
      relations: this._relationIdToId.size
    };
  }

  /**
   * Get memory usage
   */
  getMemoryUsage() {
    const typedArrayBytes = this._edges.length * 32; // 6 integers per edge
    const vertexMapBytes = this.vertices.length * 24; // 2 strings per vertex (rough estimate)
    
    return {
      typedArrays: typedArrayBytes,
      vertices: vertexMapBytes,
      total: typedArrayBytes + vertexMapBytes
    };
  }
}
