/**
 * Expression Parsing Tests
 * 
 * Tests the expression parsing capabilities of the Evidence DSL,
 * focusing on operator precedence, associativity, and complex expressions.
 */

import { DSLCompiler } from '../DSLCompiler.js';

export class ExpressionTests {
  constructor() {
    this.arbiter = null;
    this.compiler = null;
    this.testResults = [];
  }

  setup(arbiter) {
    this.arbiter = arbiter;
    this.compiler = new DSLCompiler(arbiter);
  }

  runAllTests() {
    console.log('=== Expression Parsing Tests ===\n');

    this.testArithmeticPrecedence();
    this.testLogicalPrecedence();
    this.testComparisonOperators();
    this.testTemporalExpressions();
    this.testUnaryOperators();
    this.testAttributeAccess();
    this.testFunctionCalls();
    this.testComplexExpressions();
    this.testExpressionErrors();

    return this.getTestResults();
  }

  testArithmeticPrecedence() {
    console.log('Testing Arithmetic Operator Precedence...');
    
    const testCases = [
      {
        input: '1 + 2 * 3',
        expected: 'Should evaluate as 1 + (2 * 3) = 7',
        description: 'Multiplication before addition'
      },
      {
        input: '10 - 3 * 2',
        expected: 'Should evaluate as 10 - (3 * 2) = 4',
        description: 'Multiplication before subtraction'
      },
      {
        input: '8 / 2 * 4',
        expected: 'Should evaluate as (8 / 2) * 4 = 16',
        description: 'Left-associative division and multiplication'
      },
      {
        input: '2 + 3 * 4 - 5',
        expected: 'Should evaluate as 2 + (3 * 4) - 5 = 9',
        description: 'Mixed arithmetic with correct precedence'
      },
      {
        input: '(1 + 2) * 3',
        expected: 'Should evaluate as (1 + 2) * 3 = 9',
        description: 'Parentheses override precedence'
      }
    ];

    testCases.forEach(({ input, expected, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-arithmetic-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Arithmetic precedence test: ${description}`, error);
      }
    });
  }

  testLogicalPrecedence() {
    console.log('Testing Logical Operator Precedence...');
    
    const testCases = [
      {
        input: 'true && false || true',
        expected: 'Should evaluate as (true && false) || true = true',
        description: 'AND before OR'
      },
      {
        input: 'false || true && false',
        expected: 'Should evaluate as false || (true && false) = false',
        description: 'AND before OR (alternative)'
      },
      {
        input: 'NOT true && false',
        expected: 'Should evaluate as (NOT true) && false = false',
        description: 'NOT before AND'
      },
      {
        input: 'true && NOT false',
        expected: 'Should evaluate as true && (NOT false) = true',
        description: 'NOT before AND (alternative)'
      },
      {
        input: '(true || false) && true',
        expected: 'Should evaluate as (true || false) && true = true',
        description: 'Parentheses override logical precedence'
      }
    ];

    testCases.forEach(({ input, expected, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-logical-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Logical precedence test: ${description}`, error);
      }
    });
  }

  testComparisonOperators() {
    console.log('Testing Comparison Operators...');
    
    const testCases = [
      { input: '1 == 1', description: 'Equality comparison' },
      { input: '1 != 2', description: 'Inequality comparison' },
      { input: '5 > 3', description: 'Greater than' },
      { input: '3 < 5', description: 'Less than' },
      { input: '4 >= 4', description: 'Greater than or equal' },
      { input: '4 <= 4', description: 'Less than or equal' },
      { input: '1 == 1 && 2 > 1', description: 'Comparison with logical operators' },
      { input: '1 + 2 == 3', description: 'Arithmetic in comparison' }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-comparison-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Comparison test: ${description}`, error);
      }
    });
  }

  testTemporalExpressions() {
    console.log('Testing Temporal Expressions...');
    
    const testCases = [
      { input: 'user.lastActive within 1h', description: 'Temporal within expression' },
      { input: 'user.lastLogin within 24h', description: 'Temporal within with hours' },
      { input: 'user.createdAt within 7d', description: 'Temporal within with days' },
      { input: 'user.lastActivity within 1h && user.isActive', description: 'Temporal with logical operators' }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-temporal-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Temporal test: ${description}`, error);
      }
    });
  }

  testUnaryOperators() {
    console.log('Testing Unary Operators...');
    
    const testCases = [
      { input: 'NOT true', description: 'NOT operator' },
      { input: '!false', description: 'Alternative NOT operator' },
      { input: 'NOT (true && false)', description: 'NOT with parenthesized expression' },
      { input: 'NOT user.isSuspended', description: 'NOT with attribute access' }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-unary-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Unary test: ${description}`, error);
      }
    });
  }

  testAttributeAccess() {
    console.log('Testing Attribute Access...');
    
    const testCases = [
      { input: 'user.role', description: 'Simple attribute access' },
      { input: 'user.profile.name', description: 'Nested attribute access' },
      { input: 'user.permissions[0]', description: 'Array access' },
      { input: 'user.role.permissions[0]', description: 'Nested attribute with array access' },
      { input: 'user.isActive && user.role == "admin"', description: 'Attribute access in logical expression' }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-attribute-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Attribute access test: ${description}`, error);
      }
    });
  }

  testFunctionCalls() {
    console.log('Testing Function Calls...');
    
    const testCases = [
      { input: 'hasRole(user, "admin")', description: 'Simple function call' },
      { input: 'isMember(user, group)', description: 'Function call with variables' },
      { input: 'hasPermission(user, resource, "read")', description: 'Function call with multiple arguments' },
      { input: 'hasRole(user, "admin") && isActive(user)', description: 'Multiple function calls' },
      { input: 'hasRole(user, user.role)', description: 'Function call with attribute access' }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-function-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Function call test: ${description}`, error);
      }
    });
  }

  testComplexExpressions() {
    console.log('Testing Complex Expressions...');
    
    const testCases = [
      {
        input: 'user.isActive && (hasRole(user, "admin") || hasPermission(user, resource, "read"))',
        description: 'Complex logical expression with function calls'
      },
      {
        input: 'user.balance > 100 && user.isActive && NOT user.isSuspended',
        description: 'Multiple conditions with NOT'
      },
      {
        input: 'user.lastActive within 1h && (user.role == "admin" || user.hasEmergencyAccess)',
        description: 'Temporal with logical conditions'
      },
      {
        input: 'hasRole(user, "admin") && user.isActive && NOT (user.isSuspended || user.isBlacklisted)',
        description: 'Complex negation with multiple conditions'
      },
      {
        input: 'user.score > 0.8 && user.isTrusted && user.lastActivity within 24h',
        description: 'Multiple attribute conditions with temporal'
      }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-complex-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Complex expression test: ${description}`, error);
      }
    });
  }

  testExpressionErrors() {
    console.log('Testing Expression Error Handling...');
    
    const testCases = [
      {
        input: 'user.role ==',
        description: 'Incomplete comparison should fail'
      },
      {
        input: 'user.role &&',
        description: 'Incomplete logical expression should fail'
      },
      {
        input: 'hasRole(user,)',
        description: 'Function call with missing argument should fail'
      },
      {
        input: 'user.role == "admin" &&',
        description: 'Incomplete logical expression should fail'
      }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const dsl = `evidence test() { ${input} }`;
        const result = this.compiler.compile(dsl, `test-error-${Date.now()}`);
        this.assert(!result.success, `${description} should fail to parse`);
        console.log(`  ✓ ${description} (correctly failed)`);
      } catch (error) {
        // Expected to fail
        console.log(`  ✓ ${description} (correctly failed)`);
      }
    });
  }

  assert(condition, message) {
    if (!condition) {
      throw new Error(`Assertion failed: ${message}`);
    }
  }

  fail(testName, error) {
    console.log(`  ✗ ${testName} failed: ${error.message}`);
    this.testResults.push({
      test: testName,
      status: 'FAILED',
      error: error.message
    });
  }

  getTestResults() {
    const passed = this.testResults.filter(r => r.status === 'PASSED').length;
    const failed = this.testResults.filter(r => r.status === 'FAILED').length;
    const total = this.testResults.length;

    return {
      total: total,
      passed: passed,
      failed: failed,
      success: failed === 0,
      results: this.testResults
    };
  }
}

export function runExpressionTests(arbiter) {
  const test = new ExpressionTests();
  test.setup(arbiter);
  return test.runAllTests();
}
