/**
 * Evidence Rule Tests
 * 
 * Tests the evidence rule system of the Evidence DSL,
 * including defeasible logic, pattern matching, fusion, and complex evidence composition.
 */

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

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

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

  runAllTests() {
    console.log('=== Evidence Rule Tests ===\n');

    this.testBasicEvidence();
    this.testDefeasibleLogic();
    this.testPatternMatching();
    this.testFusion();
    this.testComplexEvidence();
    this.testEvidenceErrors();

    return this.getTestResults();
  }

  testBasicEvidence() {
    console.log('Testing Basic Evidence...');
    
    const testCases = [
      {
        input: `evidence canRead(user: User, doc: Document) {
          hasRole(user, 'admin')
        }`,
        description: 'Simple evidence with function call'
      },
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          user.isActive
        }`,
        description: 'Evidence with attribute access'
      },
      {
        input: `evidence canModify(user: User, doc: Document) {
          user.isActive
          hasRole(user, 'admin')
        }`,
        description: 'Evidence with multiple conditions'
      },
      {
        input: `evidence canDelete(user: User, doc: Document) {
          owns(user, doc)
          user.isActive
        }`,
        description: 'Evidence with ownership and status'
      }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const result = this.compiler.compile(input, `test-basic-evidence-${Date.now()}`);
        this.assert(result.success, `${description} should parse successfully`);
        this.assert(result.program.evidence.length > 0, 'Should have evidence');
        console.log(`  ✓ ${description}`);
      } catch (error) {
        this.fail(`Basic evidence test: ${description}`, error);
      }
    });
  }

  testDefeasibleLogic() {
    console.log('Testing Defeasible Logic...');
    
    const testCases = [
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          ALWAYS user.isActive
        }`,
        description: 'ALWAYS rule - strict requirement'
      },
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          WHEN hasRole(user, 'admin')
        }`,
        description: 'WHEN rule - defeasible condition'
      },
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
        }`,
        description: 'WHEN/UNLESS rule - defeasible with defeater'
      },
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          REQUIRES hasClearance(user, resource.level)
        }`,
        description: 'REQUIRES rule - inverse defeater'
      },
      {
        input: `evidence canAccessCritical(user: User, resource: Resource) {
          ALWAYS user.isActive
          
          WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
          
          REQUIRES hasClearance(user, resource.level)
        }`,
        description: 'Complex defeasible logic with all rule types'
      },
      {
        input: `evidence canAccessSensitive(user: User, doc: Document) {
          ALWAYS user.isActive
          
          WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
          
          REQUIRES hasClearance(user, doc.level)
          
          fusion majority {
            user.isTrusted
            user.hasRecentActivity
          }
        }`,
        description: 'Defeasible logic with fusion'
      }
    ];

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

  testPatternMatching() {
    console.log('Testing Pattern Matching...');
    
    const testCases = [
      {
        input: `evidence canRead(user: User, doc: Document) {
          isMember(user, *group) {
            canRead(group, doc)
          }
        }`,
        description: 'Basic pattern matching with wildcard'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          isMember(user, *group) {
            canRead(group, doc)
          } limit 5
        }`,
        description: 'Pattern matching with limit'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          similar(doc, *similar) |similarity| {
            canRead(user, similar)
          } with similarity > 0.7
        }`,
        description: 'Pattern matching with binding and condition'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          similar(doc, *similar) |similarity| {
            canRead(user, similar)
          } with similarity > 0.7 limit 5
        }`,
        description: 'Pattern matching with binding, condition, and limit'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          isMember(user, *group) {
            isMember(group, *parentGroup) {
              canRead(parentGroup, doc)
            } limit 2
          } limit 3
        }`,
        description: 'Nested pattern matching'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          isFriend(user, *friend) {
            isMember(friend, *group) {
              canRead(group, doc)
            } limit 1
          } limit 5
        }`,
        description: 'Multi-hop pattern matching'
      }
    ];

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

  testFusion() {
    console.log('Testing Fusion...');
    
    const testCases = [
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          fusion min {
            hasClearance(user, resource.level)
            user.isActive
          }
        }`,
        description: 'Min fusion - all conditions must be true'
      },
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          fusion max {
            hasRole(user, 'admin')
            hasRole(user, 'superuser')
          }
        }`,
        description: 'Max fusion - any condition can be true'
      },
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          fusion majority {
            hasClearance(user, 'secret')
            user.isTrusted
            user.hasRecentActivity
          }
        }`,
        description: 'Majority fusion - most conditions must be true'
      },
      {
        input: `evidence canAccessCritical(user: User, resource: Resource) {
          fusion min {
            hasClearance(user, resource.level)
            user.isActive
            NOT user.isBlacklisted
          }
          
          fusion max {
            hasRole(user, 'admin')
            fusion majority {
              hasClearance(user, 'secret')
              user.isTrusted
              user.lastActive within 1hr
            }
          }
        }`,
        description: 'Nested fusion with different strategies'
      },
      {
        input: `evidence canAccess(user: User, resource: Resource) {
          fusion average {
            user.reputation
            user.activityScore
            user.verificationLevel
          }
        }`,
        description: 'Average fusion for numeric values'
      }
    ];

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

  testComplexEvidence() {
    console.log('Testing Complex Evidence...');
    
    const testCases = [
      {
        input: `evidence canRead(user: User, doc: Document) {
          owns(user, doc)
          
          isMember(user, *group) {
            canRead(group, doc)
          } limit 5
          
          parentOf(user, *parent) {
            canRead(parent, doc)
          } limit 3
          
          similar(doc, *similar) |similarity| {
            canRead(user, similar)
          } with similarity > 0.7 limit 5
          
          WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
        }`,
        description: 'Complex evidence with all features'
      },
      {
        input: `evidence canAccessCritical(user: User, resource: Resource) {
          ALWAYS user.isActive
          
          WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
          
          REQUIRES hasClearance(user, resource.level)
          
          fusion min {
            hasClearance(user, resource.level)
            user.isActive
            NOT user.isBlacklisted
          }
          
          fusion max {
            hasRole(user, 'admin')
            fusion majority {
              hasClearance(user, 'secret')
              user.isTrusted
              user.lastActive within 1hr
            }
          }
        }`,
        description: 'Critical access with all rule types and fusion'
      },
      {
        input: `evidence canModify(user: User, doc: Document) {
          owns(user, doc)
          
          isMember(user, *group) {
            canModify(group, doc)
          } limit 3
          
          similar(doc, *similar) |similarity| {
            canModify(user, similar)
            similar.isEditable
          } with similarity > 0.8 limit 2
          
          fusion majority {
            user.isTrusted
            user.hasRecentActivity
            doc.isPublic
          }
        }`,
        description: 'Modification access with similarity and fusion'
      }
    ];

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

  testEvidenceErrors() {
    console.log('Testing Evidence Error Handling...');
    
    const testCases = [
      {
        input: `evidence canRead(user: User, doc: Document) {
          hasRole(user, 'admin'
        }`,
        description: 'Missing closing parenthesis should fail'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          WHEN hasRole(user, 'admin') UNLESS
        }`,
        description: 'Incomplete UNLESS condition should fail'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          fusion min {
            hasRole(user, 'admin')
        }`,
        description: 'Incomplete fusion should fail'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          isMember(user, *group) {
            canRead(group, doc)
          } with
        }`,
        description: 'Incomplete with clause should fail'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          isMember(user, *group) {
            canRead(group, doc)
          } limit
        }`,
        description: 'Incomplete limit should fail'
      },
      {
        input: `evidence canRead(user: User, doc: Document) {
          invalid syntax here
        }`,
        description: 'Invalid syntax should fail'
      }
    ];

    testCases.forEach(({ input, description }) => {
      try {
        const result = this.compiler.compile(input, `test-evidence-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 runEvidenceTests(arbiter) {
  const test = new EvidenceTests();
  test.setup(arbiter);
  return test.runAllTests();
}
