import { GraphOperations } from './GraphOperations.js';
import { GraphAnalysis } from './GraphAnalysis.js';

/**
 * Main graph manager that coordinates all graph operations
 * Provides a unified interface for graph data, operations, and analysis
 */
export class GraphManager {
  constructor(arbiter, options = {}) {
    // Store reference to arbiter to access its data structures
    this.arbiter = arbiter;
    this.analysis = null; // Will be initialized when authChecker is available
    
    // Performance settings
    this.lazyStaleMarking = options.lazyStaleMarking !== undefined ? options.lazyStaleMarking : true;
    this.fastConstructionMode = options.fastConstructionMode !== undefined ? options.fastConstructionMode : false;
    this.indicesBuilt = false;
    
    // Hub settings for distance estimation
    this.maxLandmarkSample = options.maxLandmarkSample || 16;
    
    // Store references to external managers (will be set by Arbiter)
    this.traversal = null;
    this.reachabilityChecker = null;
    this.authChecker = null;
    this.operations = null;
  }

  /**
   * Initialize the graph manager with external dependencies
   * @param {object} dependencies - External dependencies
   */
  initialize(dependencies) {
    const { traversal, reachabilityChecker, authChecker } = dependencies;
    
    this.traversal = traversal;
    this.reachabilityChecker = reachabilityChecker;
    this.authChecker = authChecker;
    
    // Initialize operations and analysis now that dependencies are available
    this.operations = new GraphOperations(this.arbiter, this.traversal, this.reachabilityChecker);
    this.analysis = new GraphAnalysis(this.arbiter, this.authChecker);
  }

  // Node operations (delegate to NodeManager)
  addNode(key, type, data = {}) {
    return this.arbiter.nodeManager.addNode(key, type, data);
  }

  getNodeByKey(key) {
    const nodeId = this.arbiter.nodeIdByKey.get(key);
    return nodeId !== undefined ? this.arbiter.nodes.get(nodeId) : null;
  }

  getNodeById(nodeId) {
    return this.arbiter.nodes.get(nodeId) || null;
  }

  getNodeKeyById(nodeId) {
    return this.arbiter.keyByNodeId.get(nodeId) || null;
  }

  getNodeIdByKey(key) {
    return this.arbiter.nodeIdByKey.get(key) || null;
  }

  // Relation operations (delegate to RelationManager)
  addRelation(srcKey, relation, dstKey, options) {
    return this.arbiter.relationManager.addRelation(srcKey, relation, dstKey, options);
  }

  removeRelation(srcKey, relation, dstKey) {
    return this.arbiter.relationManager.removeRelation(srcKey, relation, dstKey);
  }

  findRelations(criteria = {}) {
    return this.arbiter.relations.filter(relation => {
      if (criteria.src !== undefined && relation.src !== criteria.src) return false;
      if (criteria.rel !== undefined && relation.rel !== criteria.rel) return false;
      if (criteria.dst !== undefined && relation.dst !== criteria.dst) return false;
      if (criteria.possibility !== undefined && relation.possibility !== criteria.possibility) return false;
      return true;
    });
  }

  getNodeRelations(nodeId, direction = 'both') {
    return this.arbiter.relations.filter(relation => {
      if (direction === 'outgoing') return relation.src === nodeId;
      if (direction === 'incoming') return relation.dst === nodeId;
      return relation.src === nodeId || relation.dst === nodeId;
    });
  }

  // Graph operations (delegate to GraphOperations)
  estimateGraphDistance(key1, key2) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.estimateGraphDistance(key1, key2);
  }

  shortestPathLength(key1, key2) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.shortestPathLength(key1, key2);
  }

  walk(startKey, steps, stepFn = () => {}, neighborWeightsFn = null) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.walk(startKey, steps, stepFn, neighborWeightsFn);
  }

  monteCarloWalk(startKey, steps, options = {}) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.monteCarloWalk(startKey, steps, options);
  }

  getSituationTree(nodeId, maxDepth = 2, visited = new Set()) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.getSituationTree(nodeId, maxDepth, visited);
  }

  findPaths(startId, endId, relation, maxDepth, visited, currentPath = [], currentPoss = 1.0, reverse = false) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.findPaths(startId, endId, relation, maxDepth, visited, currentPath, currentPoss, reverse);
  }

  // Reachability operations (delegate to GraphOperations)
  isReachable(sourceKey, targetKey) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.isReachable(sourceKey, targetKey);
  }

  getReachableNodes(sourceKey, maxResults = 1000) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.getReachableNodes(sourceKey, maxResults);
  }

  getReachingNodes(targetKey, maxResults = 1000) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.getReachingNodes(targetKey, maxResults);
  }

  getReachabilityStats() {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.getReachabilityStats();
  }

  adaptToFalsePositive(sourceKey, targetKey, falsePositiveKey, conflictingKey) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.adaptToFalsePositive(sourceKey, targetKey, falsePositiveKey, conflictingKey);
  }

  getAdaptationStats() {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.getAdaptationStats();
  }

  initializeReachabilityChecker(options = {}) {
    if (!this.operations) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.operations.initializeReachabilityChecker(options);
  }

  // Analysis operations (delegate to GraphAnalysis)
  setRelationConfig(relation, config) {
    if (!this.analysis) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.analysis.setRelationConfig(relation, config);
  }

  analyzeAccessPaths(userKey, relation, objectKey) {
    if (!this.analysis) {
      throw new Error('GraphManager not initialized with dependencies');
    }
    return this.analysis.analyzeAccessPaths(userKey, relation, objectKey);
  }

  // Hub operations (delegate to arbiter's hubIds)
  addHub(nodeId) {
    this.arbiter.hubIds.add(nodeId);
  }

  removeHub(nodeId) {
    this.arbiter.hubIds.delete(nodeId);
  }

  isHub(nodeId) {
    return this.arbiter.hubIds.has(nodeId);
  }

  getHubs() {
    return new Set(this.arbiter.hubIds);
  }

  // Performance and state management
  setLazyStaleMarking(enabled) {
    this.lazyStaleMarking = enabled;
    return this;
  }

  setFastConstructionMode(enabled) {
    this.fastConstructionMode = enabled;
    return this;
  }

  // Ensure indices are built (called before any query operation)
  _ensureIndicesBuilt() {
    if (this.fastConstructionMode && !this.indicesBuilt) {
      // This would need to be implemented with the indices manager
      // For now, just mark as built
      this.indicesBuilt = true;
    }
  }

  // Get graph statistics
  getStats() {
    return {
      nodeCount: this.arbiter.nodes.size,
      relationCount: this.arbiter.relations.length,
      nextNodeId: this.arbiter.nextNodeId,
      hubCount: this.arbiter.hubIds.size
    };
  }

  // Clear all graph data
  clear() {
    this.arbiter.nodes.clear();
    this.arbiter.nodeIdByKey.clear();
    this.arbiter.keyByNodeId.clear();
    this.arbiter.nextNodeId = 0;
    this.arbiter.relations = [];
    this.arbiter.hubIds.clear();
    this.indicesBuilt = false;
  }

  // Access to underlying data structures (for compatibility)
  get nodes() {
    return this.arbiter.nodes;
  }

  get nodeIdByKey() {
    return this.arbiter.nodeIdByKey;
  }

  get keyByNodeId() {
    return this.arbiter.keyByNodeId;
  }

  get nextNodeId() {
    return this.arbiter.nextNodeId;
  }

  get relations() {
    return this.arbiter.relations;
  }

  get hubIds() {
    return this.arbiter.hubIds;
  }

  get relationConfigs() {
    return this.analysis ? this.analysis.relationConfigs : new Map();
  }
}
