import { describe, it } from 'node:test';
import { strict as assert } from 'node:assert';
import * as fs from 'node:fs';
import * as net from 'node:net';
import * as os from 'node:os';
import * as path from 'node:path';

import { resolveNatsBinary } from '../../src/utils/runner-transport.js';
import { assertExitCode, parseLastJson, runMxCli } from '../helpers/cli-harness.js';

async function getAvailablePorts(count: number): Promise<number[]> {
  const servers = await Promise.all(
    Array.from({ length: count }, () => new Promise<net.Server>((resolve, reject) => {
      const server = net.createServer();
      server.once('error', reject);
      server.listen(0, '127.0.0.1', () => resolve(server));
    })),
  );
  const ports = servers.map((server) => {
    const address = server.address();
    assert.ok(address && typeof address === 'object');
    return address.port;
  });
  await Promise.all(servers.map((server) => new Promise<void>((resolve, reject) => {
    server.close((error) => (error ? reject(error) : resolve()));
  })));
  return ports;
}

async function createServiceFixture(rootDir: string, options?: {
  readonly conflictingTopLevelFields?: boolean;
  readonly topLevelSecretsFile?: boolean;
  readonly withEnvironment?: boolean;
}): Promise<string> {
  const packageDir = path.join(rootDir, 'sample-service');
  const runtimeDir = path.join(packageDir, 'dist', 'runtime');
  fs.mkdirSync(runtimeDir, { recursive: true });
  const conflictingTopLevelFields = options?.conflictingTopLevelFields === true;
  const topLevelSecretsFile = options?.topLevelSecretsFile === true;
  const withEnvironment = options?.withEnvironment === true;
  const runtimeFactory = {
    kind: 'factory',
    export: 'createSampleService',
    ...(conflictingTopLevelFields ? {
      mount: 'old.mount',
      config: {
        file: './config/old-config.json',
      },
      dependsOn: ['old.identity'],
      policyRef: 'policies/old',
    } : {}),
    ...(topLevelSecretsFile ? {
      secrets: {
        file: './config/manifest-secrets.json',
      },
    } : {}),
    bootstrap: {
      root: 'local.root',
      transport: {
        mode: 'local-memory',
      },
      http: {
        enabled: false,
      },
      overrides: {
        config: {
          source: 'manifest',
          nested: { enabled: false },
        },
      },
    },
    instance: {
      packageName: '@acme/sample-service',
      packageRevision: '1.0.0',
      class: 'service',
      rootKind: 'package-root',
      mount: 'sample-service.local',
      configRef: './config/service-config.json',
      secretRefs: [
        {
          name: 'apiKey',
          provider: 'system.factotum',
          ref: 'secret://sample/api-key',
          required: true,
        },
      ],
      dependsOn: ['system.identity'],
      policyRef: 'policies/standalone',
      autoStart: true,
    },
  };

  fs.writeFileSync(
    path.join(packageDir, 'package.json'),
    JSON.stringify({
      name: '@acme/sample-service',
      version: '1.0.0',
      type: 'module',
      main: './dist/runtime/index.js',
    }, null, 2),
    'utf8',
  );

  fs.writeFileSync(
    path.join(packageDir, 'matrix.json'),
    JSON.stringify({
      name: '@acme/sample-service',
      version: '1.0.0',
      namespace: 'sample',
      root: {
        type: 'SampleRoot',
        export: 'SampleRoot',
        mount: 'sample',
      },
      runtime: {
        language: 'typescript',
        entry: './dist/runtime/index.js',
        factory: runtimeFactory,
      },
      components: [
        {
          type: 'SampleService',
          export: 'SampleService',
          mount: 'worker',
          accepts: ['sample.run', 'sample.run'],
          autoStart: true,
        },
      ],
      permissions: {
        fsPolicy: 'none',
      },
    }, null, 2),
    'utf8',
  );

  fs.mkdirSync(path.join(packageDir, 'config'), { recursive: true });
  fs.writeFileSync(
    path.join(packageDir, 'config', 'old-config.json'),
    JSON.stringify({
      config: {
        old: true,
      },
    }, null, 2),
    'utf8',
  );
  fs.writeFileSync(
    path.join(packageDir, 'config', 'service-config.json'),
    JSON.stringify({
      config: {
        fromFile: true,
        nested: { file: true },
      },
    }, null, 2),
    'utf8',
  );
  fs.writeFileSync(
    path.join(packageDir, 'config', 'cli-config.json'),
    JSON.stringify({
      config: {
        fromCliFile: true,
        nested: { file: 'cli' },
      },
    }, null, 2),
    'utf8',
  );
  fs.writeFileSync(
    path.join(packageDir, 'config', 'cli-secrets.json'),
    JSON.stringify([
      {
        name: 'overrideKey',
        provider: 'system.factotum',
        ref: 'secret://sample/override-key',
      },
    ], null, 2),
    'utf8',
  );
  fs.writeFileSync(
    path.join(packageDir, 'config', 'manifest-secrets.json'),
    JSON.stringify([
      {
        name: 'manifestKey',
        provider: 'system.factotum',
        ref: 'secret://sample/manifest-key',
      },
    ], null, 2),
    'utf8',
  );

  fs.writeFileSync(
    path.join(runtimeDir, 'index.js'),
    `export async function createSampleService(overrides = {}, services = {}, _configResolution, bootstrapContext) {
      return {
        ok: true,
        effectiveConfig: overrides,
        bootContext: bootstrapContext,
        runnerRuntimeProvided: Boolean(services.runtime),
        runnerRuntimeRoot: services.runtime?.transport?.root ?? null,
        runtime: {
          async shutdown() {
            return undefined;
          }
        }
      };
    }
    `,
    'utf8',
  );

  if (withEnvironment) {
    const [natsPort, wsPort] = await getAvailablePorts(2);
    fs.mkdirSync(path.join(packageDir, '.matrix'), { recursive: true });
    fs.writeFileSync(
      path.join(packageDir, '.matrix', 'dev.environment.json'),
      JSON.stringify({
        name: 'dev',
        runtime: {
          root: 'COM.TEST.PACKAGE-ENV',
          runtimeId: 'sample-service-dev',
        },
        package: {
          publicRoot: 'sample',
        },
        nats: {
          mode: 'embedded',
          port: natsPort,
          wsPort,
          binaryPath: resolveNatsBinary(process.cwd()),
        },
      }, null, 2),
      'utf8',
    );
  }

  return packageDir;
}

function createPackageRootFixture(rootDir: string): string {
  const packageDir = path.join(rootDir, 'package-root-service');
  const runtimeDir = path.join(packageDir, 'dist', 'runtime');
  fs.mkdirSync(runtimeDir, { recursive: true });

  fs.writeFileSync(
    path.join(packageDir, 'package.json'),
    JSON.stringify({
      name: '@acme/package-root-service',
      version: '2.0.0',
      type: 'module',
      main: './dist/runtime/index.js',
    }, null, 2),
    'utf8',
  );

  fs.writeFileSync(
    path.join(packageDir, 'matrix.json'),
    JSON.stringify({
      name: '@acme/package-root-service',
      version: '2.0.0',
      runtime: {
        language: 'typescript',
        entry: './dist/runtime/index.js',
        factory: {
          kind: 'factory',
          export: 'createPackageRootService',
          bootstrap: {
            overrides: {
              mode: 'package-root',
            },
          },
          instance: {
            rootKind: 'package-root',
          },
        },
      },
      components: [
        {
          type: 'NestedService',
          export: 'NestedService',
          mount: 'nested-component-mount',
          autoStart: true,
        },
      ],
      permissions: {
        fsPolicy: 'none',
      },
    }, null, 2),
    'utf8',
  );

  fs.writeFileSync(
    path.join(runtimeDir, 'index.js'),
    `export async function createPackageRootService(overrides = {}, _services, _configResolution, bootstrapContext) {
      return {
        ok: true,
        effectiveConfig: overrides,
        bootContext: bootstrapContext,
        runtime: {
          async shutdown() {
            return undefined;
          }
        }
      };
    }
    `,
    'utf8',
  );

  return packageDir;
}

function createComponentRootFixture(
  rootDir: string,
  options: {
    name: string;
    components: Array<{ type: string; mount?: string }>;
    instance?: Record<string, unknown>;
  },
): string {
  const packageDir = path.join(rootDir, options.name);
  const runtimeDir = path.join(packageDir, 'dist', 'runtime');
  fs.mkdirSync(runtimeDir, { recursive: true });

  fs.writeFileSync(
    path.join(packageDir, 'package.json'),
    JSON.stringify({
      name: `@acme/${options.name}`,
      version: '1.0.0',
      type: 'module',
      main: './dist/runtime/index.js',
    }, null, 2),
    'utf8',
  );

  fs.writeFileSync(
    path.join(packageDir, 'matrix.json'),
    JSON.stringify({
      name: `@acme/${options.name}`,
      version: '1.0.0',
      runtime: {
        language: 'typescript',
        entry: './dist/runtime/index.js',
        factory: {
          kind: 'factory',
          export: 'createComponentRootService',
          instance: {
            rootKind: 'component',
            ...(options.instance ?? {}),
          },
        },
      },
      components: options.components.map((component) => ({
        type: component.type,
        export: component.type,
        ...(component.mount ? { mount: component.mount } : {}),
        autoStart: false,
      })),
      permissions: {
        fsPolicy: 'none',
      },
    }, null, 2),
    'utf8',
  );

  fs.writeFileSync(
    path.join(runtimeDir, 'index.js'),
    `export async function createComponentRootService(overrides = {}, _services, _configResolution, bootstrapContext) {
      return {
        ok: true,
        effectiveConfig: overrides,
        bootContext: bootstrapContext,
        runtime: {
          async shutdown() {
            return undefined;
          }
        }
      };
    }
    `,
    'utf8',
  );

  return packageDir;
}

describe('mx service command', () => {
  it('marks runtime control ready before registry registration', () => {
    const source = fs.readFileSync(path.join(process.cwd(), 'src', 'commands', 'service.ts'), 'utf8');
    const controlIndex = source.indexOf('const mountedControlMount = mounted.controlMount;');
    const readyIndex = source.indexOf("lifecycleState = 'ready';");
    const registerIndex = source.indexOf('const runtimeRegistration = await registerRunnerRuntime(');
    const liveIndex = source.indexOf("lifecycleState = 'live';");

    assert.notEqual(controlIndex, -1);
    assert.notEqual(readyIndex, -1);
    assert.notEqual(registerIndex, -1);
    assert.notEqual(liveIndex, -1);
    assert.ok(controlIndex < readyIndex, 'control actor mount result must precede ready state');
    assert.ok(readyIndex < registerIndex, 'runtime.ready must not wait on self-registration');
    assert.ok(registerIndex < liveIndex, 'live state remains after registry registration');
  });

  it('loads matrix.json runtime.factory and invokes the declared bootstrap export in check mode', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-'));
    try {
      const packageDir = await createServiceFixture(tmpRoot);
      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
        '--json',
        '--root',
        'cli.root',
        '--mount',
        'cli.mount',
        '--config',
        './config/cli-config.json',
        '--secrets',
        './config/cli-secrets.json',
        '--overrides',
        JSON.stringify({
          config: {
            nested: { enabled: true },
            cli: 'override',
          },
        }),
      ]);

      assertExitCode(result, 0);
      const payload = parseLastJson(result.logs) as {
        packageName: string;
        exportName: string;
        checked: boolean;
        root?: string;
        mount?: string;
        configRef?: string;
        secretsRef?: string;
        instance?: {
          id?: string;
          class?: string;
          mount?: string;
          rootKind?: string;
          configRef?: string;
          policyRef?: string;
          dependsOn?: string[];
          secretRefs?: Array<{
            name?: string;
            provider?: string;
            ref?: string;
            required?: boolean;
          }>;
          registrationSource?: string;
        };
        serviceInstance?: {
          id?: string;
          class?: string;
          mount?: string;
          rootKind?: string;
          configRef?: string;
          policyRef?: string;
          dependsOn?: string[];
          secretRefs?: Array<{
            name?: string;
            provider?: string;
            ref?: string;
            required?: boolean;
          }>;
          registrationSource?: string;
        };
        runtimeRegistration?: {
          runtimeId?: string;
          runtimeRoot?: string;
          registryMount?: string;
          created?: boolean;
          actorCount?: number;
          runtimeCount?: number;
        };
        bindingRegistration?: {
          runtimeId?: string;
          registryMount?: string;
          registeredCount?: number;
          bindings?: Array<{
            binding?: string;
            localMount?: string;
            packageName?: string;
            ops?: string[];
          }>;
        };
        runtimeDeregistration?: {
          runtimeId?: string;
          registryMount?: string;
          removed?: boolean;
          runtimeCount?: number;
        };
        bindingDeregistration?: {
          runtimeId?: string;
          registryMount?: string;
          removedCount?: number;
        };
        result?: {
          effectiveConfig?: {
            config?: {
              source?: string;
              cli?: string;
              nested?: { enabled?: boolean };
              fromCliFile?: boolean;
            };
          };
          runnerRuntimeProvided?: boolean;
          runnerRuntimeRoot?: string | null;
          bootContext?: {
            root?: string;
            mount?: string;
            configRef?: string;
            secretsRef?: string;
            transport?: { mode?: string };
            http?: { enabled?: boolean };
            instance?: {
              mount?: string;
              configRef?: string;
              dependsOn?: string[];
              policyRef?: string;
              secretRefs?: Array<{
                name?: string;
                provider?: string;
                ref?: string;
              }>;
              registrationSource?: string;
            };
            serviceInstance?: {
              mount?: string;
              configRef?: string;
              dependsOn?: string[];
              policyRef?: string;
              secretRefs?: Array<{
                name?: string;
                provider?: string;
                ref?: string;
              }>;
              registrationSource?: string;
            };
          };
        };
      };

      assert.equal(payload.packageName, '@acme/sample-service');
      assert.equal(payload.exportName, 'createSampleService');
      assert.equal(payload.checked, true);
      assert.equal(payload.root, 'cli.root');
      assert.equal(payload.mount, 'cli.mount');
      assert.equal(payload.configRef, './config/cli-config.json');
      assert.equal(payload.secretsRef, './config/cli-secrets.json');
      assert.equal(payload.result?.effectiveConfig?.config?.source, 'manifest');
      assert.equal(payload.result?.effectiveConfig?.config?.fromCliFile, true);
      assert.equal(payload.result?.effectiveConfig?.config?.cli, 'override');
      assert.equal(payload.result?.effectiveConfig?.config?.nested?.enabled, true);
      assert.equal(payload.result?.runnerRuntimeProvided, true);
      assert.equal(payload.result?.runnerRuntimeRoot, 'cli.root');
      assert.equal(payload.runtimeRegistration?.runtimeId, '@acme/sample-service:sample-service.local');
      assert.equal(payload.runtimeRegistration?.runtimeRoot, 'cli.root');
      assert.equal(payload.runtimeRegistration?.registryMount, 'system.runtimes');
      assert.equal(payload.runtimeRegistration?.created, true);
      // Runtime control now has one RuntimeControlActor at
      // system.runtimes.<runtimeId>. LoadedPackageActor adds one mount while
      // the separate .control mount is absent, so actorCount stays at 5.
      assert.equal(payload.runtimeRegistration?.actorCount, 5);
      assert.equal(payload.runtimeRegistration?.runtimeCount, 1);
      assert.equal(payload.bindingRegistration?.runtimeId, '@acme/sample-service:sample-service.local');
      assert.equal(payload.bindingRegistration?.registryMount, 'system.bindings');
      assert.equal(payload.bindingRegistration?.registeredCount, 2);
      assert.deepEqual(payload.bindingRegistration?.bindings, [
        {
          binding: 'sample',
          localMount: 'cli.mount',
          packageName: '@acme/sample-service',
          ops: [],
        },
        {
          binding: 'sample.worker',
          localMount: 'cli.mount.worker',
          packageName: '@acme/sample-service',
          ops: ['sample.run'],
        },
      ]);
      assert.equal(payload.runtimeDeregistration?.runtimeId, '@acme/sample-service:sample-service.local');
      assert.equal(payload.runtimeDeregistration?.registryMount, 'system.runtimes');
      assert.equal(payload.runtimeDeregistration?.removed, true);
      assert.equal(payload.runtimeDeregistration?.runtimeCount, 0);
      assert.equal(payload.bindingDeregistration?.runtimeId, '@acme/sample-service:sample-service.local');
      assert.equal(payload.bindingDeregistration?.registryMount, 'system.bindings');
      assert.equal(payload.bindingDeregistration?.removedCount, 2);
      assert.equal(payload.instance?.autoStart, true);
      assert.equal(payload.instance?.mount, 'cli.mount');
      assert.equal(payload.instance?.configRef, './config/cli-config.json');
      assert.equal(payload.instance?.rootKind, 'package-root');
      assert.equal(payload.instance?.policyRef, 'policies/standalone');
      assert.deepEqual(payload.instance?.dependsOn, ['system.identity']);
      assert.equal(payload.instance?.registrationSource, 'matrix-service');
      assert.equal(payload.instance?.secretRefs?.[0]?.name, 'overrideKey');
      assert.equal(payload.instance?.secretRefs?.[0]?.provider, 'system.factotum');
      assert.equal(payload.instance?.secretRefs?.[0]?.ref, 'secret://sample/override-key');
      assert.equal(payload.serviceInstance?.mount, 'cli.mount');
      assert.equal(payload.serviceInstance?.configRef, './config/cli-config.json');
      assert.equal(payload.serviceInstance?.rootKind, 'package-root');
      assert.equal(payload.serviceInstance?.autoStart, true);
      assert.equal(payload.serviceInstance?.policyRef, 'policies/standalone');
      assert.deepEqual(payload.serviceInstance?.dependsOn, ['system.identity']);
      assert.equal(payload.serviceInstance?.registrationSource, 'matrix-service');
      assert.equal(payload.serviceInstance?.secretRefs?.[0]?.name, 'overrideKey');
      assert.equal(payload.serviceInstance?.secretRefs?.[0]?.provider, 'system.factotum');
      assert.equal(payload.serviceInstance?.secretRefs?.[0]?.ref, 'secret://sample/override-key');
      assert.equal(payload.result?.bootContext?.root, 'cli.root');
      assert.equal(payload.result?.bootContext?.mount, 'cli.mount');
      assert.equal(payload.result?.bootContext?.configRef, './config/cli-config.json');
      assert.equal(payload.result?.bootContext?.secretsRef, './config/cli-secrets.json');
      assert.equal(payload.result?.bootContext?.transport?.mode, 'local-memory');
      assert.equal(payload.result?.bootContext?.http?.enabled, false);
      assert.equal(payload.result?.bootContext?.instance?.mount, 'cli.mount');
      assert.equal(payload.result?.bootContext?.instance?.configRef, './config/cli-config.json');
      assert.equal(payload.result?.bootContext?.instance?.autoStart, true);
      assert.deepEqual(payload.result?.bootContext?.instance?.dependsOn, ['system.identity']);
      assert.equal(payload.result?.bootContext?.instance?.policyRef, 'policies/standalone');
      assert.equal(payload.result?.bootContext?.instance?.registrationSource, 'matrix-service');
      assert.equal(payload.result?.bootContext?.instance?.secretRefs?.[0]?.name, 'overrideKey');
      assert.equal(payload.result?.bootContext?.serviceInstance?.mount, 'cli.mount');
      assert.equal(payload.result?.bootContext?.serviceInstance?.configRef, './config/cli-config.json');
      assert.equal(payload.result?.bootContext?.serviceInstance?.autoStart, true);
      assert.deepEqual(payload.result?.bootContext?.serviceInstance?.dependsOn, ['system.identity']);
      assert.equal(payload.result?.bootContext?.serviceInstance?.policyRef, 'policies/standalone');
      assert.equal(payload.result?.bootContext?.serviceInstance?.registrationSource, 'matrix-service');
      assert.equal(payload.result?.bootContext?.serviceInstance?.secretRefs?.[0]?.name, 'overrideKey');
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });

  it('loads package-local environment manifests and threads them into standalone bootstrap', async () => {
    const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-env-'));
    const packageDir = await createServiceFixture(tempRoot, { withEnvironment: true });

    const result = await runMxCli(['node', 'mx', 'service', packageDir, '--check', '--json', '--env', 'dev']);
    assertExitCode(result, 0);
    const payload = parseLastJson(result.logs) as {
      environmentName?: string;
      root?: string;
      environmentPath?: string;
      runnerTransport?: {
        mode?: string;
        root?: string;
        url?: string;
        wsUrl?: string;
        dataDir?: string;
        pidFile?: string;
      };
      runtimeRegistration?: {
        runtimeId?: string;
        runtimeRoot?: string;
        runtimeCount?: number;
      };
      bindingRegistration?: {
        runtimeId?: string;
        registryMount?: string;
        registeredCount?: number;
        bindings?: Array<{
          binding?: string;
          localMount?: string;
          packageName?: string;
          ops?: string[];
        }>;
      };
      runtimeDeregistration?: {
        runtimeId?: string;
        removed?: boolean;
        runtimeCount?: number;
      };
      bindingDeregistration?: {
        runtimeId?: string;
        removedCount?: number;
      };
      result?: {
        bootContext?: {
          environment?: {
            name?: string;
            runtime?: { root?: string };
          };
          runnerTransport?: {
            mode?: string;
            url?: string;
            wsUrl?: string;
          };
        };
      };
    };

    assert.equal(payload.environmentName, 'dev');
    assert.equal(payload.root, 'COM.TEST.PACKAGE-ENV');
    assert.equal(
      payload.environmentPath,
      path.join(packageDir, '.matrix', 'dev.environment.json'),
    );
    assert.equal(payload.runnerTransport?.mode, 'embedded');
    assert.equal(payload.runnerTransport?.root, 'COM.TEST.PACKAGE-ENV');
    assert.match(String(payload.runnerTransport?.url), /^nats:\/\/127\.0\.0\.1:\d+$/);
    assert.match(String(payload.runnerTransport?.wsUrl), /^ws:\/\/127\.0\.0\.1:\d+$/);
    assert.equal(payload.runnerTransport?.dataDir, path.join(packageDir, '.matrix', 'state', 'dev', 'nats'));
    assert.equal(payload.runnerTransport?.pidFile, path.join(packageDir, '.matrix', 'state', 'dev', 'nats-server.pid'));
    assert.equal(payload.result?.bootContext?.environment?.name, 'dev');
    assert.equal(payload.result?.bootContext?.environment?.runtime?.root, 'COM.TEST.PACKAGE-ENV');
    assert.equal(payload.result?.bootContext?.runnerTransport?.mode, 'embedded');
    assert.match(String(payload.result?.bootContext?.runnerTransport?.url), /^nats:\/\/127\.0\.0\.1:\d+$/);
    assert.match(String(payload.result?.bootContext?.runnerTransport?.wsUrl), /^ws:\/\/127\.0\.0\.1:\d+$/);
    assert.equal(payload.runtimeRegistration?.runtimeId, 'sample-service-dev');
    assert.equal(payload.runtimeRegistration?.runtimeRoot, 'COM.TEST.PACKAGE-ENV');
    assert.equal(payload.runtimeRegistration?.runtimeCount, 1);
    assert.equal(payload.bindingRegistration?.runtimeId, 'sample-service-dev');
    assert.equal(payload.bindingRegistration?.registryMount, 'system.bindings');
    assert.equal(payload.bindingRegistration?.registeredCount, 2);
    assert.deepEqual(payload.bindingRegistration?.bindings, [
      {
        binding: 'sample',
        localMount: 'sample-service.local',
        packageName: '@acme/sample-service',
        ops: [],
      },
      {
        binding: 'sample.worker',
        localMount: 'sample-service.local.worker',
        packageName: '@acme/sample-service',
        ops: ['sample.run'],
      },
    ]);
    assert.equal(payload.runtimeDeregistration?.runtimeId, 'sample-service-dev');
    assert.equal(payload.runtimeDeregistration?.removed, true);
    assert.equal(payload.runtimeDeregistration?.runtimeCount, 0);
    assert.equal(payload.bindingDeregistration?.runtimeId, 'sample-service-dev');
    assert.equal(payload.bindingDeregistration?.removedCount, 2);
  });

  it('fails clearly when standalone top-level fields are present', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-instance-conflict-'));
    try {
      const packageDir = await createServiceFixture(tmpRoot, { conflictingTopLevelFields: true });
      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
        '--json',
      ]);

      assertExitCode(result, 1);
      assert.match(result.errors.join('\n'), /top-level "mount" is no longer supported; use "instance\.mount"/i);
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });

  it('fails clearly when matrix.json runtime.factory uses top-level secrets.file', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-secrets-field-'));
    try {
      const packageDir = await createServiceFixture(tmpRoot, { topLevelSecretsFile: true });
      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
        '--json',
      ]);

      assertExitCode(result, 1);
      assert.match(result.errors.join('\n'), /top-level "secrets\.file" is no longer supported; use "instance\.secretRefs or CLI --secrets"/i);
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });

  it('fails clearly when matrix.json runtime.factory uses top-level standalone bootstrap fields', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-bootstrap-field-'));
    try {
      const packageDir = await createServiceFixture(tmpRoot);
      const manifestPath = path.join(packageDir, 'matrix.json');
      const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {
        runtime?: { factory?: Record<string, unknown> };
      };
      fs.writeFileSync(
        manifestPath,
        JSON.stringify({
          ...manifest,
          runtime: {
            ...(manifest.runtime ?? {}),
            factory: {
              ...(manifest.runtime?.factory ?? {}),
              root: 'old.root',
            },
          },
        }, null, 2),
        'utf8',
      );

      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
        '--json',
      ]);

      assertExitCode(result, 1);
      assert.match(result.errors.join('\n'), /top-level "root" is no longer supported; use "bootstrap\.root"/i);
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });

  it('fails clearly when matrix.json runtime.factory is missing', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-missing-'));
    try {
      const packageDir = path.join(tmpRoot, 'broken-service');
      fs.mkdirSync(packageDir, { recursive: true });
      fs.writeFileSync(
        path.join(packageDir, 'matrix.json'),
        JSON.stringify({
          name: '@acme/broken-service',
          version: '1.0.0',
          runtime: { language: 'typescript', entry: './dist/runtime/index.js' },
          components: [],
          permissions: { fsPolicy: 'none' },
        }, null, 2),
        'utf8',
      );

      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
      ]);

      assertExitCode(result, 1);
      assert.match(result.errors.join('\n'), /matrix\.json runtime\.factory must be a JSON object/i);
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });

  it('fails clearly when matrix.json runtime.factory instance identity disagrees with matrix.json', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-package-identity-mismatch-'));
    try {
      const packageDir = await createServiceFixture(tmpRoot);
      const manifestPath = path.join(packageDir, 'matrix.json');
      const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {
        runtime?: { factory?: { instance?: Record<string, unknown> } };
      };
      fs.writeFileSync(
        manifestPath,
        JSON.stringify({
          ...manifest,
          runtime: {
            ...(manifest.runtime ?? {}),
            factory: {
              ...(manifest.runtime?.factory ?? {}),
              instance: {
                ...(manifest.runtime?.factory?.instance ?? {}),
                packageName: '@acme/not-the-same-service',
              },
            },
          },
        }, null, 2),
        'utf8',
      );

      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
        '--json',
      ]);

      assertExitCode(result, 1);
      assert.match(
        result.errors.join('\n'),
        /instance\.packageName "@acme\/not-the-same-service" does not match matrix\.json name "@acme\/sample-service"/,
      );
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });

  it('does not invent a component mount for package-root standalone services', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-package-root-'));
    try {
      const packageDir = createPackageRootFixture(tmpRoot);
      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
        '--json',
      ]);

      assertExitCode(result, 0);
      const payload = parseLastJson(result.logs) as {
        mount?: string;
        instance?: {
          id?: string;
          mount?: string;
          rootKind?: string;
        };
        serviceInstance?: {
          id?: string;
          mount?: string;
          rootKind?: string;
        };
        result?: {
          bootContext?: {
            mount?: string;
            instance?: {
              mount?: string;
              rootKind?: string;
            };
            serviceInstance?: {
              mount?: string;
              rootKind?: string;
            };
          };
        };
      };

      assert.equal(payload.mount, undefined);
      assert.equal(payload.instance?.id, '@acme/package-root-service');
      assert.equal(payload.instance?.mount, undefined);
      assert.equal(payload.instance?.rootKind, 'package-root');
      assert.equal(payload.serviceInstance?.id, '@acme/package-root-service');
      assert.equal(payload.serviceInstance?.mount, undefined);
      assert.equal(payload.serviceInstance?.rootKind, 'package-root');
      assert.equal(payload.result?.bootContext?.mount, undefined);
      assert.equal(payload.result?.bootContext?.instance?.mount, undefined);
      assert.equal(payload.result?.bootContext?.instance?.rootKind, 'package-root');
      assert.equal(payload.result?.bootContext?.serviceInstance?.mount, undefined);
      assert.equal(payload.result?.bootContext?.serviceInstance?.rootKind, 'package-root');
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });

  it('resolves component-root standalone services by declared componentType', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-component-root-'));
    try {
      const packageDir = createComponentRootFixture(tmpRoot, {
        name: 'component-root-service',
        components: [
          { type: 'PrimaryService', mount: 'primary.mount' },
          { type: 'SecondaryService', mount: 'secondary.mount' },
        ],
        instance: {
          componentType: 'SecondaryService',
        },
      });
      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
        '--json',
      ]);

      assertExitCode(result, 0);
      const payload = parseLastJson(result.logs) as {
        mount?: string;
        instance?: {
          componentType?: string;
          mount?: string;
          rootKind?: string;
        };
        result?: {
          bootContext?: {
            mount?: string;
            instance?: {
              componentType?: string;
              mount?: string;
              rootKind?: string;
            };
          };
        };
      };

      assert.equal(payload.mount, 'secondary.mount');
      assert.equal(payload.instance?.componentType, 'SecondaryService');
      assert.equal(payload.instance?.mount, 'secondary.mount');
      assert.equal(payload.instance?.rootKind, 'component');
      assert.equal(payload.result?.bootContext?.mount, 'secondary.mount');
      assert.equal(payload.result?.bootContext?.instance?.componentType, 'SecondaryService');
      assert.equal(payload.result?.bootContext?.instance?.mount, 'secondary.mount');
      assert.equal(payload.result?.bootContext?.instance?.rootKind, 'component');
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });

  it('fails clearly when component-root standalone bootstrap is ambiguous', async () => {
    const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-component-ambiguous-'));
    try {
      const packageDir = createComponentRootFixture(tmpRoot, {
        name: 'ambiguous-component-root-service',
        components: [
          { type: 'PrimaryService', mount: 'primary.mount' },
          { type: 'SecondaryService', mount: 'secondary.mount' },
        ],
      });
      const result = await runMxCli([
        'node',
        'mx',
        'service',
        packageDir,
        '--check',
      ]);

      assertExitCode(result, 1);
      assert.match(result.errors.join('\n'), /component-root bootstrap is ambiguous/i);
    } finally {
      fs.rmSync(tmpRoot, { recursive: true, force: true });
    }
  });
});
