/**
 * @fileoverview Tests for OrdoJS Dependency Analyzer
 */

import { beforeEach, describe, expect, it } from 'vitest';
import {
    DirectiveType,
    ExpressionType,
    type AttributeNode,
    type ClientBlockNode,
    type ComponentAST,
    type ComponentNode,
    type ComputedValueNode,
    type ExpressionNode,
    type HTMLElementNode,
    type InterpolationNode,
    type MarkupBlockNode,
    type ReactiveVariableNode,
    type SourcePosition,
    type SourceRange
} from '../types/index.js';
import { DependencyAnalyzer, DependencyType, UpdateType } from './dependency-analyzer.js';

describe('DependencyAnalyzer', () => {
  let analyzer: DependencyAnalyzer;
  let mockSourceRange: SourceRange;
  let mockSourcePosition: SourcePosition;

  beforeEach(() => {
    analyzer = new DependencyAnalyzer();
    mockSourcePosition = { line: 1, column: 1, offset: 0 };
    mockSourceRange = { start: mockSourcePosition, end: mockSourcePosition };
  });

  // Helper function to create a mock expression
  function createMockExpression(
    type: ExpressionType,
    identifier?: string,
    value?: any,
    left?: ExpressionNode,
    right?: ExpressionNode,
    operator?: string
  ): ExpressionNode {
    return {
      type: 'Expression',
      expressionType: type,
      identifier,
      value,
      left,
      right,
      operator,
      range: mockSourceRange
    };
  }

  // Helper function to create a mock reactive variable
  function createMockReactiveVariable(
    name: string,
    initialValue: ExpressionNode,
    isConst = false
  ): ReactiveVariableNode {
    return {
      type: 'ReactiveVariable',
      name,
      initialValue,
      dataType: { name: 'any', isArray: false, isOptional: false, genericTypes: [] },
      isConst,
      range: mockSourceRange
    };
  }

  // Helper function to create a mock computed value
  function createMockComputedValue(
    name: string,
    expression: ExpressionNode,
    dependencies: string[] = []
  ): ComputedValueNode {
    return {
      type: 'ComputedValue',
      name,
      expression,
      dependencies,
      dataType: { name: 'any', isArray: false, isOptional: false, genericTypes: [] },
      range: mockSourceRange
    };
  }

  // Helper function to create a mock interpolation
  function createMockInterpolation(expression: ExpressionNode): InterpolationNode {
    return {
      type: 'Interpolation',
      expression,
      range: mockSourceRange
    };
  }

  // Helper function to create a mock attribute
  function createMockAttribute(
    name: string,
    value: string | ExpressionNode,
    isDirective = false,
    directiveType?: DirectiveType
  ): AttributeNode {
    return {
      type: 'Attribute',
      name,
      value,
      isDirective,
      directiveType,
      range: mockSourceRange
    };
  }

  // Helper function to create a mock HTML element
  function createMockHTMLElement(
    tagName: string,
    attributes: AttributeNode[] = [],
    children: any[] = []
  ): HTMLElementNode {
    return {
      type: 'HTMLElement',
      tagName,
      attributes,
      children,
      isSelfClosing: false,
      isVoidElement: false,
      range: mockSourceRange
    };
  }

  describe('Basic Dependency Analysis', () => {
    it('should analyze simple reactive variable dependencies', () => {
      const countVar = createMockReactiveVariable(
        'count',
        createMockExpression(ExpressionType.LITERAL, undefined, 0)
      );

      const nameVar = createMockReactiveVariable(
        'name',
        createMockExpression(ExpressionType.LITERAL, undefined, 'test')
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [countVar, nameVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [countVar, nameVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      expect(graph.nodes.size).toBe(2);
      expect(graph.nodes.has('count')).toBe(true);
      expect(graph.nodes.has('name')).toBe(true);
      expect(graph.circularDependencies).toHaveLength(0);
    });

    it('should detect dependencies in computed values', () => {
      const countVar = createMockReactiveVariable(
        'count',
        createMockExpression(ExpressionType.LITERAL, undefined, 0)
      );

      const doubleCountComputed = createMockComputedValue(
        'doubleCount',
        createMockExpression(
          ExpressionType.BINARY,
          undefined,
          undefined,
          createMockExpression(ExpressionType.IDENTIFIER, 'count'),
          createMockExpression(ExpressionType.LITERAL, undefined, 2),
          '*'
        )
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [countVar],
        computedValues: [doubleCountComputed],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [countVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      expect(graph.nodes.size).toBe(2);
      expect(graph.nodes.has('count')).toBe(true);
      expect(graph.nodes.has('doubleCount')).toBe(true);

      const doubleCountNode = graph.nodes.get('doubleCount')!;
      expect(doubleCountNode.dependencies.has('count')).toBe(true);

      const countNode = graph.nodes.get('count')!;
      expect(countNode.dependents.has('doubleCount')).toBe(true);
    });

    it('should analyze interpolation dependencies', () => {
      const countVar = createMockReactiveVariable(
        'count',
        createMockExpression(ExpressionType.LITERAL, undefined, 0)
      );

      const interpolation = createMockInterpolation(
        createMockExpression(ExpressionType.IDENTIFIER, 'count')
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [countVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [countVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [interpolation],
        range: mockSourceRange,
        children: [interpolation]
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      expect(graph.edges.some(edge =>
        edge.from === 'count' && edge.type === DependencyType.INTERPOLATION
      )).toBe(true);
    });

    it('should analyze directive dependencies', () => {
      const countVar = createMockReactiveVariable(
        'count',
        createMockExpression(ExpressionType.LITERAL, undefined, 0)
      );

      const bindAttribute = createMockAttribute(
        'bind:value',
        createMockExpression(ExpressionType.IDENTIFIER, 'count'),
        true,
        DirectiveType.BIND
      );

      const element = createMockHTMLElement('input', [bindAttribute]);

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [countVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [countVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [element],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: [element]
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      // Bind directive should create both read and write dependencies
      expect(graph.edges.some(edge =>
        edge.from === 'count' && edge.type === DependencyType.READ
      )).toBe(true);
      expect(graph.edges.some(edge =>
        edge.from === 'count' && edge.type === DependencyType.WRITE
      )).toBe(true);
    });
  });

  describe('Circular Dependency Detection', () => {
    it('should detect simple circular dependencies', () => {
      // Create variables that depend on each other: a depends on b, b depends on a
      const aVar = createMockReactiveVariable(
        'a',
        createMockExpression(ExpressionType.IDENTIFIER, 'b')
      );

      const bVar = createMockReactiveVariable(
        'b',
        createMockExpression(ExpressionType.IDENTIFIER, 'a')
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [aVar, bVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [aVar, bVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      expect(graph.circularDependencies.length).toBeGreaterThan(0);
      expect(graph.nodes.get('a')?.isCircular).toBe(true);
      expect(graph.nodes.get('b')?.isCircular).toBe(true);
      expect(analyzer.getErrors().length).toBeGreaterThan(0);
    });

    it('should detect complex circular dependencies', () => {
      // Create a -> b -> c -> a circular dependency
      const aVar = createMockReactiveVariable(
        'a',
        createMockExpression(ExpressionType.IDENTIFIER, 'c')
      );

      const bVar = createMockReactiveVariable(
        'b',
        createMockExpression(ExpressionType.IDENTIFIER, 'a')
      );

      const cVar = createMockReactiveVariable(
        'c',
        createMockExpression(ExpressionType.IDENTIFIER, 'b')
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [aVar, bVar, cVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [aVar, bVar, cVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      expect(graph.circularDependencies.length).toBeGreaterThan(0);
      expect(graph.nodes.get('a')?.isCircular).toBe(true);
      expect(graph.nodes.get('b')?.isCircular).toBe(true);
      expect(graph.nodes.get('c')?.isCircular).toBe(true);
    });

    it('should not detect false circular dependencies', () => {
      // Create a linear dependency chain: a -> b -> c
      const aVar = createMockReactiveVariable(
        'a',
        createMockExpression(ExpressionType.LITERAL, undefined, 1)
      );

      const bVar = createMockReactiveVariable(
        'b',
        createMockExpression(ExpressionType.IDENTIFIER, 'a')
      );

      const cVar = createMockReactiveVariable(
        'c',
        createMockExpression(ExpressionType.IDENTIFIER, 'b')
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [aVar, bVar, cVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [aVar, bVar, cVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      expect(graph.circularDependencies).toHaveLength(0);
      expect(graph.nodes.get('a')?.isCircular).toBe(false);
      expect(graph.nodes.get('b')?.isCircular).toBe(false);
      expect(graph.nodes.get('c')?.isCircular).toBe(false);
    });
  });

  describe('Update Order Calculation', () => {
    it('should calculate correct update order for linear dependencies', () => {
      // Create a -> b -> c dependency chain
      const aVar = createMockReactiveVariable(
        'a',
        createMockExpression(ExpressionType.LITERAL, undefined, 1)
      );

      const bVar = createMockReactiveVariable(
        'b',
        createMockExpression(ExpressionType.IDENTIFIER, 'a')
      );

      const cVar = createMockReactiveVariable(
        'c',
        createMockExpression(ExpressionType.IDENTIFIER, 'b')
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [cVar, aVar, bVar], // Intentionally out of order
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [cVar, aVar, bVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      // Update order should be a, b, c (topological order)
      expect(graph.updateOrder).toEqual(['a', 'b', 'c']);
      expect(graph.nodes.get('a')?.updateOrder).toBe(0);
      expect(graph.nodes.get('b')?.updateOrder).toBe(1);
      expect(graph.nodes.get('c')?.updateOrder).toBe(2);
    });

    it('should handle independent variables correctly', () => {
      const aVar = createMockReactiveVariable(
        'a',
        createMockExpression(ExpressionType.LITERAL, undefined, 1)
      );

      const bVar = createMockReactiveVariable(
        'b',
        createMockExpression(ExpressionType.LITERAL, undefined, 2)
      );

      const cVar = createMockReactiveVariable(
        'c',
        createMockExpression(ExpressionType.LITERAL, undefined, 3)
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [aVar, bVar, cVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [aVar, bVar, cVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      // All variables are independent, so they can be in any order
      expect(graph.updateOrder).toHaveLength(3);
      expect(graph.updateOrder).toContain('a');
      expect(graph.updateOrder).toContain('b');
      expect(graph.updateOrder).toContain('c');
    });
  });

  describe('Update Function Generation', () => {
    it('should generate update functions for reactive variables', () => {
      const countVar = createMockReactiveVariable(
        'count',
        createMockExpression(ExpressionType.LITERAL, undefined, 0)
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [countVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [countVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const updateFunctions = analyzer.generateUpdateFunctions(ast);

      expect(updateFunctions).toHaveLength(1);
      expect(updateFunctions[0].variableName).toBe('count');
      expect(updateFunctions[0].updateType).toBe(UpdateType.TEXT_CONTENT);
      expect(updateFunctions[0].code).toContain('component.state.count');
    });

    it('should generate different update types based on usage', () => {
      const countVar = createMockReactiveVariable(
        'count',
        createMockExpression(ExpressionType.LITERAL, undefined, 0)
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [countVar],
        computedValues: [],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [countVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const updateFunctions = analyzer.generateUpdateFunctions(ast);

      expect(updateFunctions).toHaveLength(1);
      expect(updateFunctions[0].code).toBeTruthy();
      expect(updateFunctions[0].code.length).toBeGreaterThan(0);
    });
  });

  describe('Complex Scenarios', () => {
    it('should handle mixed dependencies correctly', () => {
      // Create a complex scenario with multiple types of dependencies
      const countVar = createMockReactiveVariable(
        'count',
        createMockExpression(ExpressionType.LITERAL, undefined, 0)
      );

      const doubleCountComputed = createMockComputedValue(
        'doubleCount',
        createMockExpression(
          ExpressionType.BINARY,
          undefined,
          undefined,
          createMockExpression(ExpressionType.IDENTIFIER, 'count'),
          createMockExpression(ExpressionType.LITERAL, undefined, 2),
          '*'
        )
      );

      const interpolation = createMockInterpolation(
        createMockExpression(ExpressionType.IDENTIFIER, 'doubleCount')
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [countVar],
        computedValues: [doubleCountComputed],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [countVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [interpolation],
        range: mockSourceRange,
        children: [interpolation]
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      expect(graph.nodes.size).toBe(2);
      expect(graph.updateOrder).toEqual(['count', 'doubleCount']);
      expect(graph.edges.some(edge =>
        edge.from === 'count' && edge.type === DependencyType.COMPUTED
      )).toBe(true);
      expect(graph.edges.some(edge =>
        edge.from === 'doubleCount' && edge.type === DependencyType.INTERPOLATION
      )).toBe(true);
    });

    it('should handle nested expressions correctly', () => {
      const aVar = createMockReactiveVariable(
        'a',
        createMockExpression(ExpressionType.LITERAL, undefined, 1)
      );

      const bVar = createMockReactiveVariable(
        'b',
        createMockExpression(ExpressionType.LITERAL, undefined, 2)
      );

      // Create a complex nested expression: (a + b) * 2
      const complexComputed = createMockComputedValue(
        'complex',
        createMockExpression(
          ExpressionType.BINARY,
          undefined,
          undefined,
          createMockExpression(
            ExpressionType.BINARY,
            undefined,
            undefined,
            createMockExpression(ExpressionType.IDENTIFIER, 'a'),
            createMockExpression(ExpressionType.IDENTIFIER, 'b'),
            '+'
          ),
          createMockExpression(ExpressionType.LITERAL, undefined, 2),
          '*'
        )
      );

      const clientBlock: ClientBlockNode = {
        type: 'ClientBlock',
        reactiveVariables: [aVar, bVar],
        computedValues: [complexComputed],
        eventHandlers: [],
        functions: [],
        lifecycle: [],
        range: mockSourceRange,
        children: [aVar, bVar]
      };

      const markupBlock: MarkupBlockNode = {
        type: 'MarkupBlock',
        elements: [],
        textNodes: [],
        interpolations: [],
        range: mockSourceRange,
        children: []
      };

      const component: ComponentNode = {
        type: 'Component',
        name: 'TestComponent',
        props: [],
        clientBlock,
        markupBlock,
        range: mockSourceRange,
        children: [clientBlock, markupBlock]
      };

      const ast: ComponentAST = {
        component,
        dependencies: [],
        exports: [],
        sourceMap: {
          version: 3,
          sources: [],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };

      const graph = analyzer.analyze(ast);

      const complexNode = graph.nodes.get('complex')!;
      expect(complexNode.dependencies.has('a')).toBe(true);
      expect(complexNode.dependencies.has('b')).toBe(true);

      const aNode = graph.nodes.get('a')!;
      const bNode = graph.nodes.get('b')!;
      expect(aNode.dependents.has('complex')).toBe(true);
      expect(bNode.dependents.has('complex')).toBe(true);
    });
  });
});
