import { describe, it, expect } from 'vitest';
import { PathResolver } from '../PathResolver';
import { ValidationError } from '../errors';
import { IterationContext } from '../types';

describe('PathResolver', () => {
  describe('parsePath', () => {
    it('should parse simple paths', () => {
      const result = PathResolver.parsePath('phase1.set1.output');
      expect(result).toEqual([
        { name: 'phase1', choice: null, isAll: false },
        { name: 'set1', choice: null, isAll: false },
        { name: 'output', choice: null, isAll: false }
      ]);
    });

    it('should handle $ prefix', () => {
      const result = PathResolver.parsePath('$phase1.set1.output');
      expect(result).toEqual([
        { name: 'phase1', choice: null, isAll: false },
        { name: 'set1', choice: null, isAll: false },
        { name: 'output', choice: null, isAll: false }
      ]);
    });

    it('should parse paths with choices', () => {
      const result = PathResolver.parsePath('phase1.set1[auth].output');
      expect(result).toEqual([
        { name: 'phase1', choice: null, isAll: false },
        { name: 'set1', choice: 'auth', isAll: false },
        { name: 'output', choice: null, isAll: false }
      ]);
    });

    it('should parse paths with $this choices', () => {
      const result = PathResolver.parsePath('phase1.set1[$this].output');
      expect(result).toEqual([
        { name: 'phase1', choice: null, isAll: false },
        { name: 'set1', choice: '$this', isAll: false },
        { name: 'output', choice: null, isAll: false }
      ]);
    });

    it('should handle all keyword', () => {
      const result = PathResolver.parsePath('phase1.set1.all');
      expect(result).toEqual([
        { name: 'phase1', choice: null, isAll: false },
        { name: 'set1', choice: null, isAll: false },
        { name: 'all', choice: null, isAll: true }
      ]);
    });

    it('should throw on invalid path segments', () => {
      expect(() => {
        PathResolver.parsePath('phase1.set1[].output');
      }).toThrow(ValidationError);

      expect(() => {
        PathResolver.parsePath('phase1.set1[invalid[.output');
      }).toThrow(ValidationError);
    });
  });

  describe('resolveChoice', () => {
    const context: IterationContext = {
      currentIteration: 'auth',
      parentIterations: ['services']
    };

    const objectsForIteration = {
      services: {
        auth: { code: 'auth.ts' },
        user: { code: 'user.ts' }
      },
      set1: {
        auth: { output: 'auth-result' },
        user: { output: 'user-result' }
      }
    };

    it('should resolve $this to current iteration', () => {
      const segment = { name: 'services', choice: '$this', isAll: false };
      const result = PathResolver.resolveChoice(
        segment,
        context,
        objectsForIteration
      );
      expect(result).toBe('auth');
    });

    it('should throw when using $this outside iteration context', () => {
      const segment = { name: 'services', choice: '$this', isAll: false };
      expect(() => {
        PathResolver.resolveChoice(segment, {}, objectsForIteration);
      }).toThrow(ValidationError);
    });

    it('should validate choices against objectsForIteration', () => {
      const segment = { name: 'services', choice: 'invalid', isAll: false };
      expect(() => {
        PathResolver.resolveChoice(segment, context, objectsForIteration);
      }).toThrow(ValidationError);
    });

    it('should return empty string for no choice', () => {
      const segment = { name: 'services', choice: null, isAll: false };
      const result = PathResolver.resolveChoice(
        segment,
        context,
        objectsForIteration
      );
      expect(result).toBe('');
    });
  });

  describe('walkPath', () => {
    const root = {
      set1: {
        auth: { output: 'auth-result' },
        user: { output: 'user-result' }
      },
      set2: {
        output: 'direct-result'
      }
    };

    const context: IterationContext = {
      currentIteration: 'auth',
      parentIterations: ['services']
    };

    const objectsForIteration = {
      services: {
        auth: { code: 'auth.ts' },
        user: { code: 'user.ts' }
      },
      set1: {
        auth: { output: 'auth-result' },
        user: { output: 'user-result' }
      }
    };

    it('should walk simple paths', () => {
      const segments = PathResolver.parsePath('set2.output');
      const result = PathResolver.walkPath(
        root,
        segments,
        context,
        objectsForIteration
      );
      expect(result).toBe('direct-result');
    });

    it('should walk paths with choices', () => {
      const segments = PathResolver.parsePath('set1[auth].output');
      const result = PathResolver.walkPath(
        root,
        segments,
        context,
        objectsForIteration
      );
      expect(result).toBe('auth-result');
    });

    it('should handle all keyword', () => {
      const segments = PathResolver.parsePath('set1.all');
      const result = PathResolver.walkPath(
        root,
        segments,
        context,
        objectsForIteration
      );
      expect(result).toEqual([
        { output: 'auth-result' },
        { output: 'user-result' }
      ]);
    });

    it('should throw on invalid path segments', () => {
      const segments = PathResolver.parsePath('invalid.output');
      expect(() => {
        PathResolver.walkPath(root, segments, context, objectsForIteration);
      }).toThrow(ValidationError);
    });

    it('should throw on invalid choices', () => {
      const segments = PathResolver.parsePath('set1[invalid].output');
      expect(() => {
        PathResolver.walkPath(root, segments, context, objectsForIteration);
      }).toThrow(ValidationError);
    });
  });
}); 