import { PathResolver, PathSegment } from './PathResolver';
import { ValidationError } from './errors';
import { IterationContext, Workflow } from './types';

export class VariableResolver {
  private outputs: Map<string, any>;
  private globals: Record<string, any>;
  private objectsForIteration: Record<string, any>;

  constructor(workflow: Workflow) {
    this.outputs = new Map();
    this.globals = workflow.variables || {};
    this.objectsForIteration = workflow.objectsForIteration || {};
  }

  /**
   * Resolve a variable reference, handling both output paths and globals
   */
  resolve(reference: string, context: IterationContext = {}): any {
    if (reference.startsWith('$')) {
      return this.resolveOutputPath(reference, context);
    }
    return this.resolveGlobalReference(reference);
  }

  /**
   * Register an output value at a specific path
   */
  registerOutput(path: string, value: any, context: IterationContext = {}): void {
    const segments = PathResolver.parsePath(path);
    const phase = segments[0].name;

    if (!this.outputs.has(phase)) {
      this.outputs.set(phase, {});
    }

    let current = this.outputs.get(phase);
    
    // Walk through segments except the last one
    for (let i = 1; i < segments.length - 1; i++) {
      const segment = segments[i];
      const resolvedChoice = segment.choice ? 
        PathResolver.resolveChoice(segment, context, this.objectsForIteration) :
        null;

      if (!current[segment.name]) {
        current[segment.name] = {};
      }
      current = current[segment.name];

      if (resolvedChoice) {
        if (!current[resolvedChoice]) {
          current[resolvedChoice] = {};
        }
        current = current[resolvedChoice];
      }
    }

    // Handle the last segment
    const lastSegment = segments[segments.length - 1];
    const resolvedChoice = lastSegment.choice ?
      PathResolver.resolveChoice(lastSegment, context, this.objectsForIteration) :
      null;

    if (resolvedChoice) {
      if (!current[lastSegment.name]) {
        current[lastSegment.name] = {};
      }
      current[lastSegment.name][resolvedChoice] = value;
    } else {
      current[lastSegment.name] = value;
    }
  }

  private resolveOutputPath(path: string, context: IterationContext): any {
    const segments = PathResolver.parsePath(path);
    const phase = segments[0].name;

    if (!this.outputs.has(phase)) {
      throw new ValidationError(`Phase '${phase}' not found`);
    }

    return PathResolver.walkPath(
      this.outputs.get(phase),
      segments.slice(1),
      context,
      this.objectsForIteration
    );
  }

  private resolveGlobalReference(reference: string): any {
    if (!(reference in this.globals)) {
      throw new ValidationError(`Global variable '${reference}' not found`);
    }
    return this.globals[reference];
  }

  /**
   * Get all outputs for a phase
   */
  getPhaseOutputs(phase: string): any {
    return this.outputs.get(phase);
  }

  /**
   * Clear all outputs (useful for testing)
   */
  clearOutputs(): void {
    this.outputs.clear();
  }
} 