#!/usr/bin/env node

/**
 * Test to verify that apply operations always use effective states
 */

const fs = require('fs');
const { exec } = require('child_process');
const { promisify } = require('util');
const path = require('path');

const execAsync = promisify(exec);

// Change to project directory for tests
process.chdir(__dirname);

const TEST_CONFIG = 'test-apply-effective.yml';
const TEST_OUTPUT_DIR = '.test-apply-effective';

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

function cleanup() {
  if (fs.existsSync(TEST_CONFIG)) fs.unlinkSync(TEST_CONFIG);
  if (fs.existsSync(TEST_OUTPUT_DIR)) {
    fs.rmSync(TEST_OUTPUT_DIR, { recursive: true, force: true });
  }
}

async function runCommand(command) {
  try {
    const { stdout, stderr } = await execAsync(command);
    return { success: true, stdout, stderr };
  } catch (error) {
    return { success: false, stdout: error.stdout, stderr: error.stderr, error };
  }
}

function getFilesList(dir) {
  if (!fs.existsSync(dir)) return [];

  const files = [];
  function traverse(currentDir) {
    const items = fs.readdirSync(currentDir);
    for (const item of items) {
      const fullPath = path.join(currentDir, item);
      if (fs.statSync(fullPath).isDirectory()) {
        traverse(fullPath);
      } else {
        files.push(path.basename(fullPath));
      }
    }
  }
  traverse(dir);
  return files.sort();
}

function setTestOutputDir(configFile) {
  let configContent = fs.readFileSync(configFile, 'utf8');
  configContent = configContent.replace(/output_directory:\s*".*?"/, `output_directory: "${TEST_OUTPUT_DIR}"`);
  fs.writeFileSync(configFile, configContent);
}

async function runTests() {
  console.log('Testing that apply operations use effective states...\n');

  let passedTests = 0;
  let totalTests = 0;

  async function test(name, testFn) {
    totalTests++;
    cleanup(); // Clean up before each test

    try {
      await testFn();
      console.log(`✅ ${name}`);
      passedTests++;
    } catch (error) {
      console.log(`❌ ${name}: ${error.message}`);
    }
  }

  // Test 1: Apply respects explicit false overrides
  await test("Apply respects explicit false overrides", async () => {
    await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
    setTestOutputDir(TEST_CONFIG);

    // Enable collection
    await runCommand(`node awesome-copilot.js toggle collections testing-automation on --config ${TEST_CONFIG}`);

    // Explicitly disable one item from the collection
    await runCommand(`node awesome-copilot.js toggle prompts playwright-generate-test off --config ${TEST_CONFIG}`);

    // Apply
    await runCommand(`node awesome-copilot.js apply ${TEST_CONFIG}`);

    const files = getFilesList(TEST_OUTPUT_DIR);

    // Should have files from collection but NOT the explicitly disabled one
    assert(files.includes('csharp-nunit.prompt.md'), 'Should include collection items');
    assert(!files.includes('playwright-generate-test.prompt.md'), 'Should NOT include explicitly disabled items');
  });

  // Test 2: Apply includes collection items that are undefined
  await test("Apply includes collection items that are undefined", async () => {
    await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
    setTestOutputDir(TEST_CONFIG);

    // Enable collection (items remain undefined - no explicit settings)
    await runCommand(`node awesome-copilot.js toggle collections testing-automation on --config ${TEST_CONFIG}`);

    // Apply
    await runCommand(`node awesome-copilot.js apply ${TEST_CONFIG}`);

    const files = getFilesList(TEST_OUTPUT_DIR);

    // Should include all collection items since none are explicitly overridden
    assert(files.includes('playwright-generate-test.prompt.md'), 'Should include undefined collection items');
    assert(files.includes('csharp-nunit.prompt.md'), 'Should include all collection items');
    assert(files.includes('tdd-red.chatmode.md'), 'Should include collection chatmodes');
  });

  // Test 3: Apply respects explicit true overrides over disabled collections
  await test("Apply respects explicit true overrides over disabled collections", async () => {
    await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
    setTestOutputDir(TEST_CONFIG);

    // Collection remains disabled, but explicitly enable one item
    await runCommand(`node awesome-copilot.js toggle prompts playwright-generate-test on --config ${TEST_CONFIG}`);

    // Apply
    await runCommand(`node awesome-copilot.js apply ${TEST_CONFIG}`);

    const files = getFilesList(TEST_OUTPUT_DIR);

    // Should only have the explicitly enabled item
    assert(files.includes('playwright-generate-test.prompt.md'), 'Should include explicitly enabled items');
    assert(!files.includes('csharp-nunit.prompt.md'), 'Should NOT include collection items when collection disabled');
    assert(files.length === 1, 'Should only have one file');
  });

  // Test 4: Multiple collections work together through effective states
  await test("Multiple collections work together through effective states", async () => {
    await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
    setTestOutputDir(TEST_CONFIG);

    // Enable multiple collections
    await runCommand(`node awesome-copilot.js toggle collections testing-automation on --config ${TEST_CONFIG}`);
    await runCommand(`node awesome-copilot.js toggle collections frontend-web-dev on --config ${TEST_CONFIG}`);

    // Apply
    await runCommand(`node awesome-copilot.js apply ${TEST_CONFIG}`);

    const files = getFilesList(TEST_OUTPUT_DIR);

    // Should have items from both collections
    assert(files.length > 11, 'Should have items from multiple collections'); // testing-automation has 11 items
    assert(files.includes('playwright-generate-test.prompt.md'), 'Should include testing items');
  });

  // Test 5: Apply output matches effective state computation
  await test("Apply output matches effective state computation", async () => {
    await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
    setTestOutputDir(TEST_CONFIG);

    // Complex scenario: collection + explicit override + individual enable
    await runCommand(`node awesome-copilot.js toggle collections testing-automation on --config ${TEST_CONFIG}`);
    await runCommand(`node awesome-copilot.js toggle prompts playwright-generate-test off --config ${TEST_CONFIG}`);
    await runCommand(`node awesome-copilot.js toggle prompts create-readme on --config ${TEST_CONFIG}`);

    // Get list of what should be enabled according to CLI (only individual items, not collections)
    const listResult = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
    const enabledPrompts = (listResult.stdout.match(/\[✓\] (\S+)/g) || []).map(m => m.replace('[✓] ', '').split(' ')[0]);

    const instructionsResult = await runCommand(`node awesome-copilot.js list instructions --config ${TEST_CONFIG}`);
    const enabledInstructions = (instructionsResult.stdout.match(/\[✓\] (\S+)/g) || []).map(m => m.replace('[✓] ', '').split(' ')[0]);

    const chatmodesResult = await runCommand(`node awesome-copilot.js list chatmodes --config ${TEST_CONFIG}`);
    const enabledChatmodes = (chatmodesResult.stdout.match(/\[✓\] (\S+)/g) || []).map(m => m.replace('[✓] ', '').split(' ')[0]);

    const allEnabledItems = [...enabledPrompts, ...enabledInstructions, ...enabledChatmodes];

    // Apply and get actual files
    await runCommand(`node awesome-copilot.js apply ${TEST_CONFIG}`);
    const actualFiles = getFilesList(TEST_OUTPUT_DIR);

    // Extract base names for comparison
    const actualBaseNames = actualFiles.map(f => f.replace(/\.(prompt|instructions|chatmode)\.md$/, ''));

    // Every enabled item in list should have a corresponding file
    allEnabledItems.forEach(item => {
      assert(actualBaseNames.includes(item), `Item ${item} shown as enabled should have corresponding file`);
    });

    // Every file should correspond to an enabled item
    actualBaseNames.forEach(fileName => {
      assert(allEnabledItems.includes(fileName), `File ${fileName} should correspond to an enabled item`);
    });
  });

  console.log(`\nEffective States Apply Test Results: ${passedTests}/${totalTests} passed`);

  cleanup(); // Final cleanup

  if (passedTests === totalTests) {
    console.log('🎉 All effective states apply tests passed!');
    return true;
  } else {
    console.log('💥 Some effective states apply tests failed!');
    return false;
  }
}

if (require.main === module) {
  runTests().then(success => {
    process.exit(success ? 0 : 1);
  }).catch(error => {
    console.error('Test runner error:', error);
    cleanup();
    process.exit(1);
  });
}

module.exports = { runTests };
