import { ValidationError } from './errors';
import { IterationContext } from './types';

export interface PathSegment {
  name: string;
  choice?: string | null;
  isAll: boolean;
}

export class PathResolver {
  /**
   * Parse a path into segments, handling phase names, choices, and 'all' keyword
   */
  static parsePath(path: string): PathSegment[] {
    // Remove $ prefix if present
    const cleanPath = path.startsWith('$') ? path.slice(1) : path;
    const segments = cleanPath.split('.');

    return segments.map(segment => {
      // Handle 'all' keyword
      if (segment === 'all') {
        return { name: segment, choice: null, isAll: true };
      }

      // Check for choice syntax [...]
      const choiceMatch = segment.match(/^([^[]+)(?:\[([^\]]+)\])?$/);
      if (!choiceMatch) {
        throw new ValidationError(`Invalid path segment: ${segment}`);
      }

      const [, name, choice] = choiceMatch;
      return {
        name,
        choice: choice || null,
        isAll: false
      };
    });
  }

  /**
   * Resolve a path segment's choice, handling $this references
   */
  static resolveChoice(
    segment: PathSegment,
    context: IterationContext,
    objectsForIteration: Record<string, any>
  ): string {
    if (!segment.choice) {
      return '';
    }

    if (segment.choice === '$this') {
      if (!context.currentIteration) {
        throw new ValidationError('$this used outside iteration context');
      }
      return context.currentIteration;
    }

    // Validate choice exists in objectsForIteration
    const iterationObject = objectsForIteration[segment.name];
    if (!iterationObject?.[segment.choice]) {
      throw new ValidationError(
        `Invalid choice '${segment.choice}' for iteration '${segment.name}'`
      );
    }

    return segment.choice;
  }

  /**
   * Walk through a path to get or set a value
   */
  static walkPath(
    root: any,
    segments: PathSegment[],
    context: IterationContext,
    objectsForIteration: Record<string, any>
  ): any {
    let current = root;

    for (const segment of segments) {
      if (segment.isAll) {
        return Object.values(current);
      }

      if (!current[segment.name]) {
        throw new ValidationError(`Path segment '${segment.name}' not found`);
      }

      current = current[segment.name];

      if (segment.choice) {
        const resolvedChoice = this.resolveChoice(
          segment,
          context,
          objectsForIteration
        );
        if (!current[resolvedChoice]) {
          throw new ValidationError(
            `Choice '${resolvedChoice}' not found in '${segment.name}'`
          );
        }
        current = current[resolvedChoice];
      }
    }

    return current;
  }
} 