import { describe, it, expect } from 'vitest';
import { evaluateCondition, ConditionError } from '../condition-evaluator.js';

describe('evaluateCondition', () => {
  describe('simple boolean conditions', () => {
    it('evaluates boolean true in a comparison', () => {
      expect(evaluateCondition('true == true', {})).toBe(true);
    });

    it('evaluates boolean false in a comparison', () => {
      expect(evaluateCondition('false == false', {})).toBe(true);
    });

    it('evaluates true != false', () => {
      expect(evaluateCondition('true != false', {})).toBe(true);
    });

    it('evaluates boolean via node reference', () => {
      const outputs = { flag: { output: true } };
      expect(evaluateCondition('$flag.output == true', outputs)).toBe(true);
    });
  });

  describe('string equality with node references', () => {
    it('evaluates $nodeId.output == "value" as true when matching', () => {
      const outputs = { analysis: { output: 'done' } };
      expect(evaluateCondition('$analysis.output == "done"', outputs)).toBe(true);
    });

    it('evaluates $nodeId.output == "value" as false when not matching', () => {
      const outputs = { analysis: { output: 'pending' } };
      expect(evaluateCondition('$analysis.output == "done"', outputs)).toBe(false);
    });

    it('evaluates $nodeId.output != "value" correctly', () => {
      const outputs = { analysis: { output: 'pending' } };
      expect(evaluateCondition('$analysis.output != "done"', outputs)).toBe(true);
    });
  });

  describe('numeric comparisons', () => {
    it('evaluates $score.output > 5 as true', () => {
      const outputs = { score: { output: 10 } };
      expect(evaluateCondition('$score.output > 5', outputs)).toBe(true);
    });

    it('evaluates $score.output > 5 as false when score is lower', () => {
      const outputs = { score: { output: 3 } };
      expect(evaluateCondition('$score.output > 5', outputs)).toBe(false);
    });

    it('evaluates >= comparison', () => {
      const outputs = { score: { output: 5 } };
      expect(evaluateCondition('$score.output >= 5', outputs)).toBe(true);
    });

    it('evaluates < comparison', () => {
      const outputs = { score: { output: 3 } };
      expect(evaluateCondition('$score.output < 5', outputs)).toBe(true);
    });

    it('evaluates <= comparison', () => {
      const outputs = { score: { output: 5 } };
      expect(evaluateCondition('$score.output <= 5', outputs)).toBe(true);
    });
  });

  describe('AND/OR compounds', () => {
    it('evaluates AND compound: both true', () => {
      const outputs = { a: { output: 'x' }, b: { output: 'y' } };
      expect(evaluateCondition('$a.output == "x" AND $b.output == "y"', outputs)).toBe(true);
    });

    it('evaluates AND compound: one false', () => {
      const outputs = { a: { output: 'x' }, b: { output: 'z' } };
      expect(evaluateCondition('$a.output == "x" AND $b.output == "y"', outputs)).toBe(false);
    });

    it('evaluates OR compound: one true', () => {
      const outputs = { a: { output: 'x' }, b: { output: 'z' } };
      expect(evaluateCondition('$a.output == "x" OR $b.output == "y"', outputs)).toBe(true);
    });

    it('evaluates OR compound: both false', () => {
      const outputs = { a: { output: 'z' }, b: { output: 'z' } };
      expect(evaluateCondition('$a.output == "x" OR $b.output == "y"', outputs)).toBe(false);
    });

    it('evaluates mixed AND/OR with correct precedence', () => {
      const outputs = { a: { output: 'x' }, b: { output: 'y' }, c: { output: 'z' } };
      // false AND true OR true => false OR true => true (AND binds tighter)
      expect(evaluateCondition('$a.output == "wrong" AND $b.output == "y" OR $c.output == "z"', outputs)).toBe(true);
    });
  });

  describe('parenthesized expressions', () => {
    it('evaluates parenthesized expressions', () => {
      const outputs = { a: { output: 'x' }, b: { output: 'y' } };
      expect(evaluateCondition('($a.output == "x" OR $a.output == "z") AND $b.output == "y"', outputs)).toBe(true);
    });
  });

  describe('error handling', () => {
    it('throws ConditionError on invalid expressions', () => {
      expect(() => evaluateCondition('!!!invalid', {})).toThrow(ConditionError);
    });

    it('throws ConditionError on missing node reference', () => {
      expect(() => evaluateCondition('$missing.output == "x"', {})).toThrow(ConditionError);
    });

    it('throws ConditionError on node reference without field', () => {
      expect(() => evaluateCondition('$analysis == "x"', {})).toThrow(ConditionError);
    });

    it('throws ConditionError on unterminated string', () => {
      expect(() => evaluateCondition('"unterminated', {})).toThrow(ConditionError);
    });
  });

  describe('whitespace handling', () => {
    it('handles extra whitespace around operators', () => {
      const outputs = { a: { output: 'x' } };
      expect(evaluateCondition('  $a.output  ==  "x"  ', outputs)).toBe(true);
    });
  });

  describe('quoted strings with special characters', () => {
    it('handles double-quoted strings with spaces', () => {
      const outputs = { msg: { output: 'hello world' } };
      expect(evaluateCondition('$msg.output == "hello world"', outputs)).toBe(true);
    });

    it('handles single-quoted strings', () => {
      const outputs = { msg: { output: 'hello' } };
      expect(evaluateCondition("$msg.output == 'hello'", outputs)).toBe(true);
    });
  });

  describe('empty condition', () => {
    it('returns true for empty string', () => {
      expect(evaluateCondition('', {})).toBe(true);
    });

    it('returns true for whitespace-only string', () => {
      expect(evaluateCondition('   ', {})).toBe(true);
    });
  });
});