export class ArbiterChecks {
  constructor(arbiter) {
    this.arbiter = arbiter;
  }

  check(userKey, relation, objectKey, options = {}) {
    this.arbiter._ensureIndicesBuilt();
    
    // Handle backward compatibility for old API
    if (typeof options === 'boolean') {
      options = { returnHints: options };
    }
    
    // Extract epsilon/delta if present and pass through
    if (options.partialGraph && !options.partialGraphContext) {
      // Reject oversized overlays BEFORE allocating the context: the
      // caller-supplied partial graph is untrusted input, and the check is
      // the allocation point for every decision (DoS surface).
      const policy = this.arbiter.partialGraphPolicy || {};
      const rels = Array.isArray(options.partialGraph.relations) ? options.partialGraph.relations : [];
      const nodes = Array.isArray(options.partialGraph.nodes) ? options.partialGraph.nodes : [];
      const maxRelations = policy.maxRelations !== undefined ? policy.maxRelations : 2000;
      const maxNodes = policy.maxNodes !== undefined ? policy.maxNodes : 1000;
      if (rels.length > maxRelations) {
        throw new Error(`Partial graph exceeds max relations: ${rels.length} > ${maxRelations}`);
      }
      if (nodes.length > maxNodes) {
        throw new Error(`Partial graph exceeds max nodes: ${nodes.length} > ${maxNodes}`);
      }
      options.partialGraphContext = this.arbiter._createPartialGraphContext(options.partialGraph);
      if (options.now === undefined && options.partialGraphContext.now !== null) {
        options.now = options.partialGraphContext.now;
      }
    }

    const { returnHints = false, _visited = new Set(), _currentRelation = null, binary = false, epsilon, delta, clientStateId } = options;
    
    // BINARY MODE: Ultra-fast decisive authorization
    if (binary) {
      const authOptions = {
        _visited,
        _currentRelation,
        binary: true,
        clientStateId,
        ...options
      };
      // Ensure epsilon/delta are present if specified
      if (epsilon !== undefined) authOptions.epsilon = epsilon;
      if (delta !== undefined) authOptions.delta = delta;
      const result = this.arbiter.authChecker.check(userKey, relation, objectKey, authOptions);
      
      // Convert to simple binary response if needed
      if (options.simpleBinary) {
        return {
          allow: result.allow || false,
          deny: result.deny || false,
          reason: result.reason,
          evaluation: result.evaluation,
          stateId: result.stateId
        };
      }
      
      return result;
    }
    
    // Pass through complete options to preserve threshold and fastPath settings
    const authOptions = {
      _visited,
      _currentRelation,
      clientStateId,
      ...options
    };
    // Audit needs the full validity detail (sources, conflict mass) even
    // when the caller did not ask for includeMeta; the hook is opt-in, so
    // this only ever costs on audit-enabled deployments.
    if (this.arbiter._auditHook && authOptions.includeMeta === undefined) {
      authOptions.includeMeta = true;
    }
    // Ensure epsilon/delta are present if specified
    if (epsilon !== undefined) authOptions.epsilon = epsilon;
    if (delta !== undefined) authOptions.delta = delta;
    
    // First do the normal check with complete options
    const result = this.arbiter.authChecker.check(userKey, relation, objectKey, authOptions);
    
    // If hints requested and access denied, add hints
    if (returnHints && result.possibility === 0) {
      const hints = this.arbiter.graphManager.analyzeAccessPaths(userKey, relation, objectKey);
      return {
        ...result,
        hints: hints,
        hasAlternatives: hints.length > 0
      };
    }
    
    if (options.explain) return result;

    if (this.arbiter._auditHook) {
      this.arbiter._auditHook({
        timestamp: Date.now(),
        userKey,
        relation,
        objectKey,
        decision: result.possibility > 0 ? 'allow' : 'deny',
        possibility: result.possibility,
        binary: !!options.binary,
        partialGraphUsed: !!options.partialGraphContext,
        validityLabel: result.validity ? result.validity.label : null,
        sources: (result.validity && result.validity.sources) || []
      });
    }

    return result;
  }

  explain(userKey, relation, objectKey, options = {}) {
    this.arbiter._ensureIndicesBuilt();

    if (options.partialGraph && !options.partialGraphContext) {
      options.partialGraphContext = this.arbiter._createPartialGraphContext(options.partialGraph);
      if (options.now === undefined && options.partialGraphContext.now !== null) {
        options.now = options.partialGraphContext.now;
      }
    }

    const explainOptions = {
      ...options,
      explain: true,
      trackEvaluation: options.trackEvaluation !== undefined ? options.trackEvaluation : true,
      collectValues: options.collectValues !== undefined ? options.collectValues : true
    };

    if (options.fastPath === undefined) {
      explainOptions.fastPath = false;
    }

    const startedAt = Date.now();
    const result = this.check(userKey, relation, objectKey, explainOptions);
    const endedAt = Date.now();
    const config = this.arbiter.relationConfigs.get(relation) || null;
    return this.arbiter.explainSerializer.serialize({
      userKey,
      relation,
      objectKey,
      result,
      options: explainOptions,
      startedAt,
      endedAt,
      config
    });
  }
}
