/**
 * mx actor command implementation.
 *
 * Scaffolds a complete actor package with TypeScript, tests, examples, and skills.
 * Implementation: B014
 */

import * as fs from 'node:fs';
import * as path from 'node:path';
import { toKebabCase, toPascalCase, validateMatrixManifest, writeJsonFile, generatePackageJson, generateTsconfig } from '../utils/scaffold.js';

export interface IActorCommandOptions {
  readonly cwd?: string;
  readonly outputDir?: string;
}

export interface IActorCommandResult {
  readonly rootDir: string;
  readonly className: string;
  readonly mount: string;
  readonly workspacePath?: string;
}

function actorTemplate(className: string, matrixActorImport: string): string {
  return [
    `import { MatrixActor } from ${JSON.stringify(matrixActorImport)};`,
    '',
    `export class ${className} extends MatrixActor {`,
    '  static override accepts = {',
    "    getStatus: {},",
    '  };',
    '',
    '  static override emits = {',
    "    statusChanged: { status: 'string' },",
    '  };',
    '',
    "  private _status = 'ready';",
    '',
    '  protected onGetStatus(): { status: string } {',
    '    return { status: this._status };',
    '  }',
    '}',
    '',
  ].join('\n');
}

function actorIndexTemplate(className: string): string {
  return [
    `export { ${className} } from './${className}.js';`,
    `export { createStandalone${className} } from './create-standalone-${className}.js';`,
    '',
  ].join('\n');
}

function actorFactoryTemplate(className: string, mount: string, matrixActorImport: string): string {
  const factoryName = `createStandalone${className}`;
  const actorFile = `./${className}.js`;
  return [
    `import { InMemoryBroker, InMemoryTransport, MatrixRuntime } from ${JSON.stringify(matrixActorImport)};`,
    '',
    `import { ${className} } from ${JSON.stringify(actorFile)};`,
    '',
    'interface IStandaloneBootContext {',
    '  readonly root?: string;',
    '  readonly mount?: string;',
    '}',
    '',
    'interface IStandaloneRunnerServices {',
    '  readonly runtime?: MatrixRuntime;',
    '}',
    '',
    'function readTrimmedString(value: unknown): string | undefined {',
    "  if (typeof value !== 'string') {",
    '    return undefined;',
    '  }',
    '  const trimmed = value.trim();',
    '  return trimmed.length > 0 ? trimmed : undefined;',
    '}',
    '',
    'function requiredStandaloneRoot(requestedRoot: string | undefined, runtime: MatrixRuntime): string {',
    '  const runtimeRoot = readTrimmedString(runtime.transport.root);',
    '  const root = requestedRoot ?? runtimeRoot;',
    '  if (!root) {',
    `    throw new Error('${factoryName} requires bootContext.root or a runtime transport root');`,
    '  }',
    '  return root;',
    '}',
    '',
    `export async function ${factoryName}(`,
    '  _overrides: Record<string, unknown> = {},',
    '  services?: IStandaloneRunnerServices,',
    '  _config?: unknown,',
    '  bootContext: IStandaloneBootContext = {},',
    '): Promise<{',
    '  runtime: MatrixRuntime;',
    `  actor: ${className};`,
    '  root: string;',
    '  mount: string;',
    '}> {',
    '  const requestedRoot = readTrimmedString(bootContext.root);',
    '  const runtime = services?.runtime ?? new MatrixRuntime({',
    '    transport: new InMemoryTransport(new InMemoryBroker(), {',
    `      name: ${JSON.stringify(`${mount}-standalone`)},`,
    '      ...(requestedRoot ? { root: requestedRoot } : {}),',
    '    }),',
    '    logging: false,',
    '  });',
    '  const root = requiredStandaloneRoot(requestedRoot, runtime);',
    `  const mount = readTrimmedString(bootContext.mount) ?? ${JSON.stringify(mount)};`,
    `  const actor = await runtime.createSupervised(${className}, mount);`,
    '  return { runtime, actor, root, mount };',
    '}',
    '',
  ].join('\n');
}

function actorFactoryManifest(className: string, mount: string): Record<string, unknown> {
  return {
    kind: 'factory',
    export: `createStandalone${className}`,
    instance: {
      id: `RUNTIME-HOST-${mount.toUpperCase().replace(/[^A-Z0-9]+/g, '-')}`,
      class: 'service',
      rootKind: 'component',
      componentType: className,
      autoStart: true,
    },
  };
}

function actorTestTemplate(className: string): string {
  return [
    "import { describe, it } from 'node:test';",
    "import { strict as assert } from 'node:assert';",
    `import { ${className} } from '../src/${className}.ts';`,
    '',
    `describe('${className}', () => {`,
    "  it('declares getStatus command', () => {",
    `    assert.ok(Object.prototype.hasOwnProperty.call(${className}.accepts, 'getStatus'));`,
    '  });',
    '});',
    '',
  ].join('\n');
}

function actorSkillTemplate(name: string, className: string): string {
  return [
    '---',
    `name: ${name}`,
    'intents:',
    '  - action: query',
    '    dataType: status',
    '    category: system',
    '---',
    '',
    `# ${className}`,
    '',
    'Returns actor status for orchestration and health checks.',
    '',
  ].join('\n');
}

export async function actorCommand(name: string, options: IActorCommandOptions = {}): Promise<IActorCommandResult> {
  const normalizedName = toKebabCase(name);
  if (!normalizedName) {
    throw new Error('Actor name must not be empty');
  }

  const className = `${toPascalCase(normalizedName)}Actor`;
  const mount = normalizedName;
  const cwd = path.resolve(options.cwd ?? process.cwd());
  const rootDir = path.resolve(options.outputDir ?? path.join(cwd, normalizedName));
  const matrixActorImport = '@open-matrix/core';

  fs.mkdirSync(path.join(rootDir, 'src'), { recursive: true });
  fs.mkdirSync(path.join(rootDir, 'examples'), { recursive: true });
  fs.mkdirSync(path.join(rootDir, 'skills'), { recursive: true });
  fs.mkdirSync(path.join(rootDir, 'tests'), { recursive: true });

  const manifest = {
    name: `@matrix/${normalizedName}`,
    version: '0.1.0',
    description: `${className} generated by mx actor`,
    runtime: {
      language: 'typescript',
      entry: 'dist/index.js',
      factory: actorFactoryManifest(className, mount),
    },
    components: [
      {
        type: className,
        export: className,
        mount,
        surface: 'headless',
        accepts: ['getStatus'],
        emits: ['statusChanged'],
      },
    ],
    permissions: { fsPolicy: 'none' },
  };

  const validation = validateMatrixManifest(manifest);
  if (!validation.valid) {
    throw new Error(`Generated matrix.json failed validation: ${validation.errors.join('; ')}`);
  }

  writeJsonFile(path.join(rootDir, 'matrix.json'), manifest);
  writeJsonFile(path.join(rootDir, 'package.json'), generatePackageJson(normalizedName, { service: true }));
  writeJsonFile(path.join(rootDir, 'tsconfig.json'), generateTsconfig({ service: true }));
  fs.writeFileSync(
    path.join(rootDir, 'src', `${className}.ts`),
    actorTemplate(className, matrixActorImport),
    'utf8'
  );
  fs.writeFileSync(
    path.join(rootDir, 'src', `create-standalone-${className}.ts`),
    actorFactoryTemplate(className, mount, matrixActorImport),
    'utf8'
  );
  fs.writeFileSync(path.join(rootDir, 'src', 'index.ts'), actorIndexTemplate(className), 'utf8');
  writeJsonFile(path.join(rootDir, 'examples', 'get-status.mxq'), {
    label: 'Get Status',
    operation: 'getStatus',
    payload: {},
    description: `Runs ${className}.getStatus`,
  });
  fs.writeFileSync(
    path.join(rootDir, 'skills', `${normalizedName}.skill.md`),
    actorSkillTemplate(normalizedName, className),
    'utf8'
  );
  fs.writeFileSync(path.join(rootDir, 'tests', `${normalizedName}.spec.ts`), actorTestTemplate(className), 'utf8');

  console.log(`Created actor scaffold at ${rootDir}`);
  return { rootDir, className, mount };
}
