import fs from 'fs/promises';
import path from 'path';
import { WorkflowExecutor } from './src/vl/workflow-executor.js';

let passed = 0;
let failed = 0;

function test(name, fn) {
  return Promise.resolve()
    .then(fn)
    .then(() => {
      console.log(`  ✓ ${name}`);
      passed++;
    })
    .catch((err) => {
      console.log(`  ✗ ${name}: ${err.message}`);
      failed++;
    });
}

function assert(cond, msg) {
  if (!cond) throw new Error(msg || 'Assertion failed');
}

function assertIncludes(list, value, msg) {
  if (!list.includes(value)) {
    throw new Error(msg || `Expected ${JSON.stringify(value)} in ${JSON.stringify(list)}`);
  }
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function json(obj) {
  return JSON.stringify(obj);
}

function normalizeMessages(messages = []) {
  return messages.map((msg) => ({
    role: msg.role,
    content: msg.content,
    text: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
  }));
}

function findObjectMessage(messages, predicate) {
  for (const msg of messages) {
    const value = msg.content;
    if (value && typeof value === 'object' && !Array.isArray(value) && predicate(value)) {
      return value;
    }
  }
  return null;
}

function createFixture(projectName = 'SmokeApp') {
  const services = [
    {
      id: 'ItemService',
      domainId: 'ItemService',
      filePath: 'Services/ItemService.vs',
      path: 'Services/ItemService.vs',
      methods: [{ id: 'listItems', isPublic: true, params: '', returnType: '[OBJECT]' }],
    },
    {
      id: 'OrderService',
      domainId: 'OrderService',
      filePath: 'Services/OrderService.vs',
      path: 'Services/OrderService.vs',
      methods: [{ id: 'listOrders', isPublic: true, params: '', returnType: '[OBJECT]' }],
    },
  ];
  const components = [
    { id: 'ItemCard', filePath: 'ExtComponents/ItemCard.cp', path: 'ExtComponents/ItemCard.cp' },
    { id: 'OrderBadge', filePath: 'ExtComponents/OrderBadge.cp', path: 'ExtComponents/OrderBadge.cp' },
  ];
  const sections = [
    {
      id: 'HomePage',
      filePath: 'Sections/HomePage.sc',
      path: 'Sections/HomePage.sc',
      consumesServices: ['ItemService'],
      usesComponents: ['ItemCard'],
    },
    {
      id: 'OrdersPage',
      filePath: 'Sections/OrdersPage.sc',
      path: 'Sections/OrdersPage.sc',
      consumesServices: ['OrderService'],
      usesComponents: ['OrderBadge'],
    },
  ];
  const apps = [
    { id: 'MainApp', appId: 'MainApp', filePath: 'Apps/MainApp.vx', path: 'Apps/MainApp.vx', routes: [{ path: '/', section: 'HomePage' }] },
    { id: 'AdminApp', appId: 'AdminApp', filePath: 'Apps/AdminPortalShell.vx', path: 'Apps/AdminPortalShell.vx', routes: [{ path: '/orders', section: 'OrdersPage' }] },
  ];

  const fileManifest = {
    databaseFile: { id: 'MainDB', path: `Database/${projectName}.vdb`, type: 'vdb', sourceId: 'MainDB' },
    themeFile: { id: 'Theme', path: 'Theme/Theme.vth', type: 'vth', sourceId: 'Theme' },
    serviceFiles: services.map(({ id, path: filePath }) => ({ id, path: filePath, type: 'vs', sourceId: id })),
    componentFiles: components.map(({ id, path: filePath }) => ({ id, path: filePath, type: 'cp', sourceId: id })),
    sectionFiles: sections.map(({ id, path: filePath }) => ({ id, path: filePath, type: 'sc', sourceId: id })),
    appFiles: apps.map(({ id, appId, path: filePath }) => ({ id, appId, path: filePath, type: 'vx', sourceId: id })),
  };
  fileManifest.files = [
    fileManifest.databaseFile,
    fileManifest.themeFile,
    ...fileManifest.serviceFiles,
    ...fileManifest.componentFiles,
    ...fileManifest.sectionFiles,
    ...fileManifest.appFiles,
  ];
  fileManifest.references = [
    { from: 'Sections/HomePage.sc', to: 'Services/ItemService.vs', refType: 'consumes' },
    { from: 'Sections/OrdersPage.sc', to: 'Services/OrderService.vs', refType: 'consumes' },
    { from: 'Apps/MainApp.vx', to: 'Sections/HomePage.sc', refType: 'route' },
    { from: 'Apps/AdminApp.vx', to: 'Sections/OrdersPage.sc', refType: 'route' },
  ];

  return {
    prdJson: {
      projectName,
      summary: 'parallel smoke test',
      targetLang: 'zh-CN',
    },
    projectMeta: {
      specVersion: 'ProjectMeta/1.0',
      projectName,
      projectDescription: 'parallel smoke test',
      vlVersion: '3.5',
      config: {
        defaultDevice: 'Phone',
        defaultResolution: '375x812',
        themeFile: 'Theme/Theme.vth',
        colorScheme: 'light',
      },
      database: {
        file: `Database/${projectName}.vdb`,
      },
      theme: {
        file: 'Theme/Theme.vth',
      },
      dataSchema: {
        tables: [
          { id: 'Item', fields: [{ name: 'id', type: 'INT', constraints: ['PK'] }, { name: 'title', type: 'STRING' }] },
          { id: 'Order', fields: [{ name: 'id', type: 'INT', constraints: ['PK'] }, { name: 'status', type: 'STRING' }] },
        ],
        relations: [],
      },
      services: services.map(({ path: filePath, ...rest }) => ({ ...rest, filePath })),
      components: components.map(({ path: filePath, ...rest }) => ({ ...rest, filePath })),
      sections: sections.map(({ path: filePath, ...rest }) => ({ ...rest, filePath })),
      apps: apps.map(({ path: filePath, ...rest }) => ({ ...rest, filePath })),
    },
    blueprint: {
      projectName,
      valueDomains: { orderStatus: ['new', 'paid'] },
      roles: [{ id: 'Admin' }, { id: 'User' }],
      entities: [{ id: 'Item' }, { id: 'Order' }],
      apps: apps.map(({ appId, routes }) => ({ appId, pages: routes.map((route) => ({ id: route.section, sectionId: route.section })) })),
      features: ['list-items', 'list-orders'],
    },
    dataSchema: {
      tables: [
        { id: 'Item', fields: [{ name: 'id', type: 'INT' }, { name: 'title', type: 'STRING' }], indexes: [], testData: [] },
        { id: 'Order', fields: [{ name: 'id', type: 'INT' }, { name: 'status', type: 'STRING' }], indexes: [], testData: [] },
      ],
      relations: [],
    },
    layoutPlan: {
      apps: apps.map(({ appId, routes, path: filePath }) => ({
        appId,
        filePath,
        routes,
        layoutTree: { type: 'stack', items: routes.map((route) => route.section) },
      })),
      sections: sections.map(({ id }) => ({ id, structure: ['Header', 'List'] })),
      components: components.map(({ id }) => ({ id, previewSize: 'medium' })),
    },
    contractSheet: {
      serviceDomains: services.map(({ path: filePath, ...rest }) => ({ ...rest, filePath })),
      sectionContracts: sections.map(({ path: filePath, ...rest }) => ({ ...rest, filePath })),
      componentContracts: components.map(({ path: filePath, ...rest }) => ({ ...rest, filePath })),
    },
    stateBehavior: {
      appStates: apps.map(({ appId }) => ({ appId, globalVars: ['$ready(BOOL)'], initSequence: ['load'] })),
      sectionBehaviors: sections.map(({ id }) => ({ id, keyStates: ['$items([OBJECT])'], serviceBindings: [], initBehavior: [], internalMethods: [] })),
      eventWiring: [],
      modalTriggers: [],
    },
    intentSpec: {
      projectName,
      summary: 'parallel smoke test',
      domains: ['Item', 'Order'],
      project: { customTheme: true },
      apps: apps.map(({ appId }) => ({ appId })),
    },
    domainSchema: {
      entities: [
        { id: 'Item', fields: [{ name: 'id', type: 'INT' }, { name: 'title', type: 'STRING' }] },
        { id: 'Order', fields: [{ name: 'id', type: 'INT' }, { name: 'status', type: 'STRING' }] },
      ],
      relations: [],
    },
    serviceContractMap: {
      services: services.map(({ id, domainId, path: filePath }) => ({ id, domainId, path: filePath, filePath })),
    },
    serviceMapJson: {
      serviceDomains: services.map(({ id, domainId, path: filePath }) => ({ id, domainId, filePath })),
    },
    uiContractMap: {
      components: components.map(({ id, path: filePath }) => ({ id, path: filePath, filePath })),
      sections: sections.map(({ id, path: filePath }) => ({ id, path: filePath, filePath })),
      apps: apps.map(({ appId, path: filePath }) => ({ appId, path: filePath })),
    },
    uiMapJson: {
      components: components.map(({ id, path: filePath }) => ({ id, filePath })),
      sections: sections.map(({ id, path: filePath }) => ({ id, filePath })),
      apps: apps.map(({ id, appId, path: filePath }) => ({ id, appId, filePath })),
    },
    moduleManifest: {
      databaseFile: { id: 'MainDB', path: `Database/${projectName}.vdb`, type: 'vdb' },
      themeFile: { id: `${projectName}Theme`, path: `Theme/${projectName}.vth`, type: 'vth' },
      serviceFiles: services.map(({ id, domainId, path: filePath }) => ({ id, domainId, path: filePath, type: 'vs' })),
      componentFiles: components.map(({ id, path: filePath }) => ({ id, path: filePath, type: 'cp' })),
      sectionFiles: sections.map(({ id, path: filePath }) => ({ id, path: filePath, type: 'sc' })),
      appFiles: apps.map(({ id, appId, path: filePath }) => ({ id, appId, path: filePath, type: 'vx' })),
      files: fileManifest.files,
      references: fileManifest.references,
    },
    dependencyGraph: {
      nodes: [
        { id: 'ItemService', type: 'service' },
        { id: 'OrderService', type: 'service' },
        { id: 'HomePage', type: 'section' },
        { id: 'OrdersPage', type: 'section' },
      ],
      edges: [
        { from: 'HomePage', to: 'ItemService' },
        { from: 'OrdersPage', to: 'OrderService' },
      ],
    },
    qualityGateSpec: {
      rules: [{ id: 'no-missing-files', severity: 'error' }],
    },
    executionPlan: {
      tasks: [
        { id: 'gen-db', type: 'database' },
        { id: 'gen-theme', type: 'theme' },
        { id: 'gen-services', type: 'parallel' },
      ],
      parallelGroups: [['gen-db', 'gen-theme', 'gen-services']],
    },
    fileManifest,
    vdbCode: '// VL_VERSION:3.5\nDATABASE main\nTABLE Item { id INT, title STRING }\nTABLE Order { id INT, status STRING }\n',
    vthCode: `// VL_VERSION:3.5\nTHEME ${projectName} {\n  COLOR primary = #006B5F\n}\n`,
  };
}

function serviceCode(id) {
  return `// VL_VERSION:3.5\nSERVICE ${id} {\n  METHOD list(): [OBJECT]\n}\n`;
}

function componentCode(id) {
  return `// VL_VERSION:3.5\nCOMPONENT ${id} {\n  VIEW { Text("${id}") }\n}\n`;
}

function sectionCode(id) {
  return `// VL_VERSION:3.5\nSECTION ${id} {\n  INIT { }\n}\n`;
}

function appCode(id) {
  return `// VL_VERSION:3.5\nAPP ${id} {\n  ROUTES { }\n}\n`;
}

function createFakeLLM(fixture, probe, options = {}) {
  return {
    async call(params) {
      const messages = normalizeMessages(params.messages);
      const allText = messages.map((msg) => msg.text).join('\n');
      const target = findObjectMessage(messages, (value) => (
        value.domainId || value.filePath || value.path || value.id || value.appId
      ));

      probe.active++;
      probe.maxActive = Math.max(probe.maxActive, probe.active);

      try {
        await sleep(options.delayMs || 80);

        const failId = options.failId;
        const targetId = target?.domainId || target?.id || target?.appId || target?.path || target?.filePath || '';
        if (failId && targetId.includes(failId)) {
          throw new Error(`forced failure for ${failId}`);
        }

        if (allText.includes('Generate the complete ProjectMeta JSON')) {
          return { content: json(fixture.projectMeta), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Requirement:') && allText.includes('Target Language:')) {
          return { content: json(fixture.prdJson), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Blueprint.json')) {
          return { content: json(fixture.blueprint), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate DataSchema.json')) {
          return { content: json(fixture.dataSchema), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate LayoutPlan.json')) {
          return { content: json(fixture.layoutPlan), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate FileManifest.json')) {
          return { content: json(fixture.fileManifest), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate ContractSheet.json')) {
          return { content: json(fixture.contractSheet), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate StateBehavior.json')) {
          return { content: json(fixture.stateBehavior), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Process/Specs/IntentSpec.json')) {
          return { content: json(fixture.intentSpec), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Process/Specs/DomainSchema.json')) {
          return { content: json(fixture.domainSchema), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Process/Specs/ServiceContractMap.json')) {
          return { content: json(fixture.serviceContractMap), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Process/Specs/QualityGateSpec.json')) {
          return { content: json(fixture.qualityGateSpec), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Process/Specs/UIContractMap.json')) {
          return { content: json(fixture.uiContractMap), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Process/Specs/ModuleManifest.json')) {
          return { content: json(fixture.moduleManifest), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Process/Specs/DependencyGraph.json')) {
          return { content: json(fixture.dependencyGraph), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate Process/Run/ExecutionPlan.json')) {
          return { content: json(fixture.executionPlan), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('PRD:') && !allText.includes('VDB:') && !allText.includes('ServiceMap:')) {
          return { content: fixture.vdbCode, model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('PRD:') && allText.includes('VDB:')) {
          return { content: json(fixture.serviceMapJson), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('PRD:') && allText.includes('ServiceMap:')) {
          return { content: json(fixture.uiMapJson), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate the .vdb database schema file') || allText.includes('Generate complete VL .vdb source code')) {
          return { content: fixture.vdbCode, model: 'fake-parallel-smoke', usage: {} };
        }
        if (
          allText.includes('Generate the .vth theme file') ||
          allText.includes('Generate complete VL .vth source code') ||
          allText.includes('Generate complete VL .vth theme file')
        ) {
          return { content: fixture.vthCode, model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate the .vs service domain file for:') || allText.includes('Generate complete VL .vs source code')) {
          return { content: serviceCode(target?.domainId || target?.id || 'UnknownService'), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('DomainItem:')) {
          return { content: serviceCode(target?.domainId || target?.id || 'UnknownService'), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate the .cp component file for:') || allText.includes('Generate complete VL .cp source code')) {
          return { content: componentCode(target?.id || 'UnknownComponent'), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('ComponentItem:')) {
          return { content: componentCode(target?.id || 'UnknownComponent'), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate complete VL .sc source code') || allText.includes('Generate the .sc section file for:')) {
          return { content: sectionCode(target?.id || 'UnknownSection'), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('SectionItem:')) {
          return { content: sectionCode(target?.id || 'UnknownSection'), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('Generate complete VL .vx source code') || allText.includes('Generate the .vx app file for:')) {
          return { content: appCode(target?.appId || target?.id || 'UnknownApp'), model: 'fake-parallel-smoke', usage: {} };
        }
        if (allText.includes('AppItem:')) {
          return { content: appCode(target?.appId || target?.id || 'UnknownApp'), model: 'fake-parallel-smoke', usage: {} };
        }

        throw new Error(`Unhandled fake LLM prompt: ${allText.slice(0, 200)}`);
      } finally {
        probe.active--;
      }
    },
  };
}

async function collectFiles(dir, prefix = '') {
  const entries = await fs.readdir(path.join(dir, prefix), { withFileTypes: true }).catch(() => []);
  const files = [];
  for (const entry of entries) {
    const rel = path.join(prefix, entry.name);
    if (entry.isDirectory()) {
      files.push(...await collectFiles(dir, rel));
    } else {
      files.push(rel);
    }
  }
  return files.sort();
}

async function runExistingWorkflow(file, options = {}) {
  const workflowPath = path.join(process.cwd(), '.vl-code', 'workflows', file);
  const workflow = JSON.parse(await fs.readFile(workflowPath, 'utf8'));
  const workDir = path.join('/tmp/vlcode-lite-parallel-workflow-test', `${file.replace(/\.json$/, '')}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
  const fixture = createFixture(options.projectName);
  const probe = options.sharedProbe || { active: 0, maxActive: 0 };
  const errors = [];
  const done = [];

  await fs.rm(workDir, { recursive: true, force: true });
  await fs.mkdir(workDir, { recursive: true });

  const executor = new WorkflowExecutor({ workDir, model: 'fake-parallel-smoke' });
  executor._resolveDocCenterDocs = async () => {};
  executor._buildLLMAdapter = function buildLLMAdapter() {
    return createFakeLLM(fixture, probe, options);
  };

  await executor.execute(workflow, {
    userRequest: 'Generate a small inventory app',
    userRequirement: 'Generate a small inventory app',
    targetLang: 'zh-CN',
  }, {
    onNodeDone: (evt) => done.push(evt.nodeId),
    onNodeError: (evt) => errors.push(evt.nodeId),
  });

  return {
    workDir,
    files: await collectFiles(workDir),
    errors,
    done,
    maxActive: probe.maxActive,
  };
}

const concurrentWorkflow = {
  version: '3.16',
  name: 'ConcurrentWorkflowSmoke',
  steps: [
    {
      id: 'LLM_First',
      in: {
        stream: true,
        messages: [{ role: 'user', content: 'Generate the .vdb database schema file based on the databases defined in ProjectMeta.' }],
      },
      out: { '$first': '=_result' },
      next: 'LLM_Second',
    },
    {
      id: 'LLM_Second',
      in: {
        stream: true,
        messages: [{ role: 'user', content: 'Generate the .vdb database schema file based on the databases defined in ProjectMeta.' }],
      },
      out: { '$second': '=_result' },
      next: 'Stop_End',
    },
    { id: 'Stop_End' },
  ],
};

async function runConcurrentInstance(workDir, sharedProbe) {
  const fixture = createFixture('ConcurrentApp');
  const executor = new WorkflowExecutor({ workDir, model: 'fake-parallel-smoke' });
  executor._resolveDocCenterDocs = async () => {};
  executor._buildLLMAdapter = function buildLLMAdapter() {
    return createFakeLLM(fixture, sharedProbe, { delayMs: 120 });
  };
  await fs.rm(workDir, { recursive: true, force: true });
  await fs.mkdir(workDir, { recursive: true });
  await executor.execute(concurrentWorkflow, {}, {});
}

console.log('\n── Parallel Workflow Codegen Smoke ──');

await test('parallel-codegen has real internal concurrency and writes all code files', async () => {
  const result = await runExistingWorkflow('parallel-codegen.json');
  assert(result.maxActive >= 4, `Expected internal concurrency >=4, got ${result.maxActive}`);
  assert(result.errors.length === 0, `Unexpected errors: ${result.errors.join(', ')}`);
  for (const rel of [
    '.vl-code/ProjectMeta.json',
    'Process/ProjectMeta.json',
    'Database/SmokeApp.vdb',
    'Theme/Theme.vth',
    'Services/ItemService.vs',
    'Services/OrderService.vs',
    'ExtComponents/ItemCard.cp',
    'ExtComponents/OrderBadge.cp',
    'Sections/HomePage.sc',
    'Sections/OrdersPage.sc',
    'Apps/MainApp.vx',
    'Apps/AdminPortalShell.vx',
  ]) {
    assertIncludes(result.files, rel);
  }
});

await test('meta-direct-codegen keeps DB and Theme fanout working', async () => {
  const result = await runExistingWorkflow('meta-direct-codegen.json');
  assert(result.maxActive >= 2, `Expected concurrency >=2, got ${result.maxActive}`);
  assert(result.errors.length === 0, `Unexpected errors: ${result.errors.join(', ')}`);
  for (const rel of [
    '.vl-code/ProjectMeta.json',
    'Database/SmokeApp.vdb',
    'Theme/Theme.vth',
    'Services/ItemService.vs',
    'Services/OrderService.vs',
    'ExtComponents/ItemCard.cp',
    'ExtComponents/OrderBadge.cp',
    'Sections/HomePage.sc',
    'Sections/OrdersPage.sc',
    'Apps/MainApp.vx',
    'Apps/AdminPortalShell.vx',
  ]) {
    assertIncludes(result.files, rel);
  }
});

await test('3-file-codegen generates auxiliary specs and target VL files', async () => {
  const result = await runExistingWorkflow('3-file-codegen.json');
  assert(result.errors.length === 0, `Unexpected errors: ${result.errors.join(', ')}`);
  for (const rel of [
    'Process/Artifacts/PRD.json',
    'Process/Artifacts/ServiceMap.json',
    'Process/Artifacts/UIMap.json',
    'Database/SmokeApp.vdb',
    'Theme/Theme.vth',
    'Services/ItemService.vs',
    'Services/OrderService.vs',
    'ExtComponents/ItemCard.cp',
    'ExtComponents/OrderBadge.cp',
    'Sections/HomePage.sc',
    'Sections/OrdersPage.sc',
    'Apps/MainApp.vx',
    'Apps/AdminPortalShell.vx',
  ]) {
    assertIncludes(result.files, rel);
  }
});

await test('6-file-codegen runs parallel spec fanout and parallel code generation', async () => {
  const result = await runExistingWorkflow('6-file-codegen.json');
  assert(result.maxActive >= 4, `Expected concurrency >=4, got ${result.maxActive}`);
  assert(result.errors.length === 0, `Unexpected errors: ${result.errors.join(', ')}`);
  for (const rel of [
    'Process/Specs/Blueprint.json',
    'Process/Specs/DataSchema.json',
    'Process/Specs/LayoutPlan.json',
    'Process/Specs/FileManifest.json',
    'Process/Specs/ContractSheet.json',
    'Process/Specs/StateBehavior.json',
    'Database/SmokeApp.vdb',
    'Theme/Theme.vth',
    'Services/ItemService.vs',
    'Services/OrderService.vs',
    'ExtComponents/ItemCard.cp',
    'ExtComponents/OrderBadge.cp',
    'Sections/HomePage.sc',
    'Sections/OrdersPage.sc',
    'Apps/MainApp.vx',
    'Apps/AdminPortalShell.vx',
  ]) {
    assertIncludes(result.files, rel);
  }
});

await test('9-file-codegen runs full parallel generation fanout', async () => {
  const result = await runExistingWorkflow('9-file-codegen.json');
  assert(result.maxActive >= 2, `Expected concurrency >=2, got ${result.maxActive}`);
  assert(result.errors.length === 0, `Unexpected errors: ${result.errors.join(', ')}`);
  for (const rel of [
    'Process/Specs/IntentSpec.json',
    'Process/Specs/DomainSchema.json',
    'Process/Specs/ServiceContractMap.json',
    'Process/Specs/QualityGateSpec.json',
    'Process/Specs/UIContractMap.json',
    'Process/Specs/ModuleManifest.json',
    'Process/Specs/DependencyGraph.json',
    'Process/Run/ExecutionPlan.json',
    'Database/SmokeApp.vdb',
    'Theme/Theme.vth',
    'Services/ItemService.vs',
    'Services/OrderService.vs',
    'ExtComponents/ItemCard.cp',
    'ExtComponents/OrderBadge.cp',
    'Sections/HomePage.sc',
    'Sections/OrdersPage.sc',
    'Apps/MainApp.vx',
    'Apps/AdminPortalShell.vx',
  ]) {
    assertIncludes(result.files, rel);
  }
});

await test('parallel component branch failure does not abort sibling code generation', async () => {
  const result = await runExistingWorkflow('parallel-codegen.json', { failId: 'OrderBadge' });
  assert(result.errors.length >= 1, 'Expected one skipped branch error');
  assertIncludes(result.files, 'ExtComponents/ItemCard.cp');
  assert(!result.files.includes('ExtComponents/OrderBadge.cp'), 'Failed branch file should be absent');
  assertIncludes(result.files, 'Services/ItemService.vs');
  assertIncludes(result.files, 'Apps/MainApp.vx');
});

await test('two executor instances can execute concurrently without state bleed', async () => {
  const sharedProbe = { active: 0, maxActive: 0 };
  const dirA = '/tmp/vlcode-lite-parallel-workflow-test/concurrent-a';
  const dirB = '/tmp/vlcode-lite-parallel-workflow-test/concurrent-b';

  await Promise.all([
    runConcurrentInstance(dirA, sharedProbe),
    runConcurrentInstance(dirB, sharedProbe),
  ]);

  assert(sharedProbe.maxActive >= 2, `Expected cross-instance concurrency >=2, got ${sharedProbe.maxActive}`);
});

console.log(`\n── Results ──\n\n  ${passed} passed, ${failed} failed\n`);
process.exit(failed > 0 ? 1 : 0);
