import { describe, test } from 'node:test';
import assert from 'node:assert/strict';
import { DSLCompiler } from '../../src/ast/DSLCompiler.js';
import { parse } from '../../src/ast/parser/GeneratedParser.js';
import { RuleGenerator } from '../../src/ast/generator/RuleGenerator.js';

function createMockArbiter() {
  const relationConfigs = new Map();
  return {
    relationConfigs,
    setRelationConfig(relation, config) {
      relationConfigs.set(relation, config);
    }
  };
}

describe('DSL Compiler', () => {
  const arbiter = createMockArbiter();
  const compiler = new DSLCompiler(arbiter);

  test('Basic parsing', () => {
    const dsl = `
      definition Employee {
        role: string
        isActive: boolean
      }

      fact hasRole(user: Employee, role: string)

      evidence canRead(user: Employee, doc: Account) {
        hasRole(user, 'admin')
      }
    `;

    const result = compiler.compile(dsl, 'test-basic');
    assert.ok(result.success, 'Basic parsing should succeed');
    assert.ok(result.program !== null, 'Program should be created');
    assert.ok(result.generatedRules.size > 0, 'Rules should be generated');
  });

  test('Basic compilation', () => {
    const dsl = `
      definition Employee {
        role: string
        isActive: boolean
      }

      fact hasRole(user: Employee, role: string) CACHE eager

      evidence canRead(user: Employee, doc: Account) {
        hasRole(user, 'admin')
      }
    `;

    const result = compiler.compile(dsl, 'test-compilation');
    assert.ok(result.success, 'Basic compilation should succeed');
    assert.ok(result.generatedRules.has('canRead'), 'canRead rule should be generated');

    const canReadConfig = result.generatedRules.get('canRead');
    assert.ok(canReadConfig.type === 'direct', 'canRead should be direct rule');
    assert.ok(canReadConfig.relation === 'hasRole', 'canRead should use hasRole relation');
  });

  test('Complex DSL compilation', () => {
    const dsl = `
      definition Employee {
        role: string
        isActive: boolean
        clearance: string BEHAVES {
          blurring adaptive confidence_95
        } CACHE eager
      }

      fact hasRole(user: Employee, role: string) CACHE eager
      fact isMember(user: Employee, group: Device) transitive CACHE lazy
      fact owns(user: Employee, doc: Account) CACHE eager

      evidence canRead(user: Employee, doc: Account) {
        owns(user, doc)
        hasRole(user, 'admin')
      }

      evidence canAccessCritical(user: Employee, resource: AuthSession) {
        fusion min {
          hasRole(user, 'admin'),
          hasRole(user, 'superadmin')
        }

        fusion max {
          hasRole(user, 'admin'),
          hasRole(user, 'secret')
        }
      }
    `;

    const result = compiler.compile(dsl, 'test-complex');
    assert.ok(result.success, 'Complex DSL compilation should succeed');
    assert.ok(result.generatedRules.has('canRead'), 'canRead rule should be generated');
    assert.ok(result.generatedRules.has('canAccessCritical'), 'canAccessCritical rule should be generated');

    const canReadConfig = result.generatedRules.get('canRead');
    assert.ok(canReadConfig.type === 'logical', 'canRead should be logical rule');
  });

  test('Error handling', () => {
    const invalidDSL = `
      definition Employee {
        role: string
        // Missing closing brace

      fact hasRole(user: Employee, role: string)
      // Missing semicolon

      evidence canRead(user: Employee, doc: Account) {
        // Invalid syntax
        invalid syntax here
      }
    `;

    const result = compiler.compile(invalidDSL, 'test-error');
    assert.ok(!result.success, 'Invalid DSL should fail');
    assert.ok(result.errors.length > 0, 'Should have error messages');
  });

  test('Program management', () => {
    const dsl1 = `
      definition Employee {
        role: string
        isActive: boolean
      }

      fact hasRole(user: Employee, role: string)
      evidence canRead(user: Employee, doc: Account) {
        hasRole(user, 'admin')
      }
    `;

    const dsl2 = `
      definition Employee {
        role: string
        isActive: boolean
      }

      fact hasBalance(user: Employee, amount: number)
      evidence canWithdraw(user: Employee, amount: number) {
        hasBalance(user, amount)
      }
    `;

    const result1 = compiler.compile(dsl1, 'test-auth');
    assert.ok(result1.success, 'First program should compile');

    const programs = { 'auth': dsl1, 'finance': dsl2 };
    const result2 = compiler.compileMultiple(programs);
    assert.ok(result2.success, 'Multiple programs should compile');

    const authProgram = compiler.getCompiledProgram('test-auth');
    assert.ok(authProgram !== null, 'Should retrieve compiled program');

    const removed = compiler.removeCompiledProgram('test-auth');
    assert.ok(removed, 'Should remove program');

    compiler.clearCompiledPrograms();
    const allPrograms = compiler.getAllCompiledPrograms();
    assert.ok(allPrograms.size === 0, 'Should clear all programs');
  });

  test('Validation', () => {
    const validDSL = `
      definition Employee {
        role: string
        isActive: boolean
      }

      fact hasRole(user: Employee, role: string)

      evidence canRead(user: Employee, doc: Account) {
        hasRole(user, 'admin')
      }
    `;

    const invalidDSL = `
      definition Employee {
        role: string
        // Missing closing brace

      fact hasRole(user: Employee, role: string)
      // Missing semicolon
    `;

    const validResult = compiler.validate(validDSL);
    assert.ok(validResult.success, 'Valid DSL should pass validation');

    const invalidResult = compiler.validate(invalidDSL);
    assert.ok(!invalidResult.success, 'Invalid DSL should fail validation');
    assert.ok(invalidResult.errors.length > 0, 'Should have validation errors');
  });

  test('Rule generation', () => {
    const dsl = `
      definition Employee {
        role: string
        isActive: boolean
      }

      fact hasRole(user: Employee, role: string) CACHE eager
      fact isMember(user: Employee, group: Device) transitive CACHE lazy

      evidence canRead(user: Employee, doc: Account) {
        hasRole(user, 'admin')
      }

      evidence canAccess(user: Employee, doc: Account) {
        hasRole(user, 'reader')
      }
    `;

    const result = compiler.compile(dsl, 'test-rules');
    assert.ok(result.success, 'Rule generation should succeed');

    const canReadConfig = result.generatedRules.get('canRead');
    assert.ok(canReadConfig.type === 'direct', 'canRead should be direct rule');

    const canAccessConfig = result.generatedRules.get('canAccess');
    assert.ok(canAccessConfig.type === 'direct', 'canAccess should be direct rule');
  });

  test('Multiple programs', () => {
    const programs = {
      'auth': `
        definition Employee {
          role: string
          isActive: boolean
        }

        fact hasRole(user: Employee, role: string)
        evidence canRead(user: Employee, doc: Account) {
          hasRole(user, 'admin')
        }
      `,
      'finance': `
        definition Employee {
          role: string
          isActive: boolean
        }

        fact hasBalance(user: Employee, amount: number)
        evidence canWithdraw(user: Employee, amount: number) {
          hasBalance(user, amount)
        }
      `,
      'invalid': `
        // Invalid syntax
        invalid syntax here
      `
    };

    const result = compiler.compileMultiple(programs);
    assert.ok(!result.success, 'Should fail due to invalid program');
    assert.ok(result.errors.length > 0, 'Should have errors');
    assert.ok(result.results.auth.success, 'Auth program should succeed');
    assert.ok(result.results.finance.success, 'Finance program should succeed');
    assert.ok(!result.results.invalid.success, 'Invalid program should fail');
  });
});
