/**
 * Property-Based Tests: Config Wizard Registry Invariants
 *
 * Feature: config-wizard
 *
 * Tests the structural invariants of the VARIABLE_DESCRIPTORS registry
 * from `configure.js`.
 *
 * Validates: Requirements 2.1, 2.4, 2.5
 */

const {
  VARIABLE_DESCRIPTORS,
  GROUP_ORDER
} = require('../../configure.js');

// --- Property 1: Descriptor registry uniqueness ---
describe('Property 1: Descriptor registry uniqueness', () => {
  /**
   * **Validates: Requirements 2.5**
   *
   * Every variable name appears exactly once across all groups in the
   * VARIABLE_DESCRIPTORS registry.
   */
  test('every variable name appears exactly once in the registry', () => {
    const names = VARIABLE_DESCRIPTORS.map(d => d.name);
    const nameSet = new Set(names);

    // No duplicates: set size equals array length
    expect(nameSet.size).toBe(names.length);

    // Each name appears exactly once
    const nameCounts = {};
    for (const name of names) {
      nameCounts[name] = (nameCounts[name] || 0) + 1;
    }
    for (const [name, count] of Object.entries(nameCounts)) {
      expect(count).toBe(1);
    }
  });

  test('no variable is assigned to multiple groups', () => {
    const nameToGroups = {};
    for (const desc of VARIABLE_DESCRIPTORS) {
      if (!nameToGroups[desc.name]) {
        nameToGroups[desc.name] = [];
      }
      nameToGroups[desc.name].push(desc.group);
    }

    for (const [name, groups] of Object.entries(nameToGroups)) {
      expect(groups.length).toBe(1);
    }
  });
});

// --- Property 2: Group presentation order ---
describe('Property 2: Group presentation order', () => {
  /**
   * **Validates: Requirements 2.1**
   *
   * Consecutive descriptors have non-decreasing group index in GROUP_ORDER,
   * ensuring variables are presented in group order.
   */
  test('consecutive descriptors have non-decreasing group index', () => {
    for (let i = 1; i < VARIABLE_DESCRIPTORS.length; i++) {
      const prevGroupIndex = GROUP_ORDER.indexOf(VARIABLE_DESCRIPTORS[i - 1].group);
      const currGroupIndex = GROUP_ORDER.indexOf(VARIABLE_DESCRIPTORS[i].group);

      // Both groups must exist in GROUP_ORDER
      expect(prevGroupIndex).toBeGreaterThanOrEqual(0);
      expect(currGroupIndex).toBeGreaterThanOrEqual(0);

      // Current group index must be >= previous group index
      expect(currGroupIndex).toBeGreaterThanOrEqual(prevGroupIndex);
    }
  });

  test('all descriptor groups are present in GROUP_ORDER', () => {
    const descriptorGroups = new Set(VARIABLE_DESCRIPTORS.map(d => d.group));
    for (const group of descriptorGroups) {
      expect(GROUP_ORDER).toContain(group);
    }
  });
});

// --- Property 3: Required-before-optional ordering ---
describe('Property 3: Required-before-optional ordering', () => {
  /**
   * **Validates: Requirements 2.4**
   *
   * Within each group, all required descriptors precede optional ones
   * in the registry ordering.
   */
  test('within each group, all required descriptors precede optional ones', () => {
    for (const group of GROUP_ORDER) {
      const groupDescriptors = VARIABLE_DESCRIPTORS.filter(d => d.group === group);

      let seenOptional = false;
      for (const desc of groupDescriptors) {
        if (desc.required) {
          // Once we've seen an optional, no more required should appear
          expect(seenOptional).toBe(false);
        } else {
          seenOptional = true;
        }
      }
    }
  });

  test('required count + optional count equals total for each group', () => {
    for (const group of GROUP_ORDER) {
      const groupDescriptors = VARIABLE_DESCRIPTORS.filter(d => d.group === group);
      const requiredCount = groupDescriptors.filter(d => d.required).length;
      const optionalCount = groupDescriptors.filter(d => !d.required).length;

      expect(requiredCount + optionalCount).toBe(groupDescriptors.length);
    }
  });
});
